Cooking with Maven and OSGi
Chapter 1
EXPLORE MORE
EXPLORE MORE
1.1 Introduction
OSGi is a stable, widely used framework for developing and deploying component-based systems. If you've been using Eclipse, you've already been using an OSGi container for a few years, and the pluggable and configurable nature of the Eclipse platform is a product of the OSGi standards. As a concept and as an approach, OSGi is a proven standard with an installed user base of millions and growing. It might feel like an emerging standard because solid tool support is just now emerging. Prior to the last year, you might have encountered OSGi "builds", but they were usually experienced via an IDE (such as Eclipse).
Many are starting to view OSGi as the perfect solution both for deploying server-side applications and client-side GUI applications. If you need a GUI front-end or a web application to interface with a database back-end, there is a rich array of standard components to choose from in the OSGi community. Before OSGi-based servers, if you wanted a full fledged application server with a Transaction provider and JMS integration, you had to either run some large, monolithic application server like WebSphere, or you had to hack some custom components to a lightweight servlet-container such as Apache Tomcat or Jetty. Today, you wouldn't install Jetty from scratch, you will install it as an OSGi bundle in an OSGi container such as Apache Felix. If you needed an embedded database like Apache Derby or a transaction provider, you can now tell your container what bundles to deploy and every component is developed to operate in a standard operating environment. Once you've figured out how to instantiate an empty OSGi container such as Apache Felix there is no simpler deployment mechanism for your application, and once you've connected it to Sonatype Nexus Repository, you've instantly solved the deployment problem in Java. This chapter takes you through the steps required to start using Maven and OSGi together and how to use Sonatype Nexus Repository to support the distribution and deployment opportunities that are possible with an OSGi bundle repository (OBR).
In this chapter, we introduce some tools and techniques you can use to start developing OSGi components (or bundles) using Maven. The following recipes focus on the intersection of Apache Felix, the OPS4J project, and the Sonatype Nexus Repository as a bridge between Maven repositories and OSGi Bundle repositories. At the end of this chapter, you should have a clear picture of how to start developing OSGi-based applications using Maven.
1.2. Generating an OSGi Project with Maven
1.2.1. Task
You need to create a Maven multi-module project that allows you to develop a modular, OSGi-based web application.
1.2.2. Action
Use the Maven Pax Plugin from OPS4J, and call the create-project goal. The following command-line will create a multi-module project with a groupId of org.sonatype.mcookbook, an artifactId of osgi-project, and a version of 1.0-SNAPSHOT:
DgroupId=org.sonatype.mcookbook \
-DartifactId=osgi-project \
-Dversion=1.0-SNAPSHOT
[INFO] Scanning for projects...
[INFO] artifact org.ops4j:maven-pax-plugin: checking for updates from central
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project [INFO] task-segment: [org.ops4j:maven-pax-plugin:create-project] (aggregator-style)
[INFO] ------------------------------------------------------------------------ ...
[INFO] [pax:create-project]
[INFO] Selecting latest archetype release within version range [1,2)
[INFO] artifact org.ops4j.pax.construct:maven-archetype-osgi-project: checking for updates from central
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating Archetype: maven-archetype-osgi-project:1.0.3
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: packageName, Value: org.sonatype.mcookbook.osgi-project
[INFO] Parameter: archetypeVersion, Value: 1.0.3
[INFO] Parameter: groupId, Value: org.sonatype.mcookbook
[INFO] Parameter: archetypeArtifactId, Value: maven-archetype-osgi-project
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: archetypeGroupId, Value: org.ops4j.pax.construct
[INFO] Parameter: basedir, Value: ~/examples/osgi
[INFO] Parameter: package, Value: org.sonatype.mcookbook.osgi-project
[INFO] Parameter: artifactId, Value: osgi-project
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] Archetype created in dir: ~/examples/osgi/osgi-project
Once you've generated an OSGi project using the Pax Plugin, you will have the following directory structure:

Figure 1.1 Project structure created by the OPS4J Pax Plugin
If you want to verify that you can build the project successfully, run mvn clean install:
[INFO] Reactor build order:
[INFO] org.sonatype.mcookbook.osgi-project (OSGi project)
[INFO] osgi-project - plugin configuration
[INFO] osgi-project - wrapper instructions
[INFO] osgi-project - bundle instructions
[INFO] osgi-project - imported bundles
[INFO] ------------------------------------------------------------------------
[INFO] Building org.sonatype.mcookbook.osgi-project (OSGi project)
[INFO] task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean]
[INFO] [site:attach-descriptor]
[INFO] [install:install]
[INFO] Installing ~/examples/osgi/osgi-project/target/pom-transformed. xml to ~/.m2/repository/org/sonatype/mcookbook/osgi-project/1.0-SNAPS HOT/osgi-project-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Building osgi-project - plugin configuration
[INFO] task-segment: [clean, install] ...
NOTE
Instead of executing these goals manually, you can also download and install the Pax-Construct scripts which can be used to automate the process of creating a new OSGi project with the Pax plugin. For more information, see the Pax Construct Quickstart page.
1.2.3. Detail
The generated multi-module project structure contains a parent project with the supplied groupId, artifactId, and version, and a few submodules:
osgi-project/pom.xml
This is the parent POM
osgi-project/poms/compiled/pom.xml
This POM serves as the parent POM to all of the compiled OSGi components you will add to this osgi-project Maven project.
osgi-project/poms/wrappers/pom.xml
If you can't find a particular library as an OSGi component, the Pax Construct tools allow you to wrap an existing dependency artifact into an OSGi bundle. The configuration for these wrapped bundles is stored in this POM.
osgi-project/provision/pom.xml
This POM configures the Apache Felix runtime environment that you can start by running the pax:provision goal. If you need to import a bundle into your runtime environment, this is the POM that contains a reference to the corresponding Maven dependency.
NOTE
The osgi-project/runner directory is not created until you run the pax:provision goal as shown in Section 1.3, “Starting an OSGi Container”.
1.3 Starting an OSGi Container
1.3.1. Task
You created an OSGi project, now you want to see it running in an OSGi environment. You need to start an OSGi container and load imported bundles into the runtime environment.
1.3.2. Action
First, create an OSGi project by executing the commands in Section 1.2, “Generating an OSGi Project with Maven”. Once you do this, you have a structure that has been designed to support OSGi development with Maven. To start the Apache Felix container, run mvn install pax:provision from the osgi-project/ directory:
[INFO] Scanning for projects...
[INFO] Reactor build order:
[INFO] org.sonatype.mcookbook.osgi-project (OSGi project)
[INFO] osgi-project - plugin configuration
[INFO] osgi-project - wrapper instructions
[INFO] osgi-project - bundle instructions
[INFO] osgi-project - imported bundles
... [INFO] [pax:provision]
Pax Runner (1.0.0) from OPS4J - http://www.ops4j.org
----------------------------------------------------
-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [~/examples/osgi/osgi-project/runner/deploy-pom.xml]
-> Scan bundles from [scan-pom:file:/~/examples/osgi/osgi-project/runner/deploy-pom.xml]
-> Preparing framework [Felix 1.8.0]
-> Downloading bundles...
-> Using execution environment [J2SE-1.5]
-> Runner has successfully finished his job!
Welcome to Felix.
=================
->
NOTE
Instead of executing these goals manually, you can also download and install the Pax-Construct scripts which can be used to automate the process of running Apache Felix with the Pax plugin. For more information, see the Pax Construct Quickstart page.
1.3.3 Detail
When you started up Felix, notice that the line directly following the execution of the pax:provision goal says "No bundles found!". By running pax:provision, you've started the Felix OSGi service platform. Felix uses the configuration from a properties file and then scans two deploy-pom.xml files for bundles that it should download and install. Finding none, it presents a simple prompt and awaits orders. At this point, we have an empty container that isn't running any custom logic or downloading any OSGi bundles.
The first time you ran pax:provision, the Pax plugin created a runner directory under osgi-project which captures the configuration of the runtime environment. This runner directory contains the following files and directories:
osgi-project/runner/deploy-pom.xml
This POM file is generated using Pax-Construct, it contains some configuration parameters for the Apache Felix container.
osgi-project/runner/bundles
This directory contains OSGi bundles which have been downloaded for use in Apache Felix.
osgi-project/runner/felix
This directory contains the runtime files that are required for Apache Felix, this includes a config.ini file and a cache directory.
Once you have the console for Felix loaded, you can control the platform, load components from remote repositories, list all of the running components, and start and stop components. Try executing the command help to see a list of available commands:
-> help | |
bundlelevel <level> <id> ... | <id> | - set or get bundle start level. |
cd [<base-URL>] | - change or display base URL. |
exports <id> ... | - list exported packages. |
headers [<id> ...] | - display bundle header properties. |
help | - display impl commands. |
imports <id> ... | - list imported packages. |
install <URL> [<URL> ...] | - install bundle(s). |
ps [-l | -s | -u] | - list installed bundles. |
refresh [<id> ...] | - refresh packages. |
requirers <id> ... | - list requiring bundles. |
requires <id> ... | - list required bundles. |
resolve [<id> ...] | - attempt to resolve the specified bundles. |
scr help | - Declarative Services Runtime |
services [-u] [-a] [<id> ...] | - list registered or used services. |
shutdown | - shutdown framework. |
start [-t] <id> [<id> <URL> ...] | - start bundle(s). |
startlevel [<level>] | - get or set framework start level. |
stop [-t] <id> [<id> ...] | - stop bundle(s). |
uninstall <id> [<id> ...] | - uninstall bundle(s). |
update <id> [<URL>] | - update bundle. |
version | - display version of framework. |
If you execute the ps command, you can see a list of bundles with IDs that are running in the Felix container. You can start and stop bundles by running start and stop followed by the ID of the specific bundle you want to control. You can also install and uninstall bundles in the running container. Over the next few recipes we're going to fill in this picture a bit by wrapping existing JARs, importing existing OSGi bundles, and writing a custom OSGi service.
1.3.4. Resources
For more information about Apache Felix, see https://felix.apache.org/documentation/index.html.
1.4. Importing OSGi Bundles with Maven
1.4.1 Task
You want to configure your OSGi runtime to load bundles on startup.
1.4.2 Action
Import OSGi bundles from the Maven repository. While the Maven repository wasn't designed for OSGi like the OSGi Bundle Repositoriy (OBR) repository format, it contains a few components which contain the appropriate metadata to be referenced as OSGi components. To configure your runtime environment to load these components at runtime, you will need to invoke the pax:import-bundle goal.
As shown in the following screen listing, you are going to import several OSGi bundles from the Maven repository. First you are going to install the Apache Felix Web Management Console and then you are going to install some of its requirements. To install the first OSGi bundle run the following command:
-DgroupId=org.apache.felix \
-DartifactId=org.apache.felix.webconsole \
-Dversion=1.2.8
[INFO] Scanning for projects
[INFO] Reactor build order:
[INFO] org.sonatype.mcookbook.osgi-project (OSGi project)
[INFO] osgi-project - plugin configuration
[INFO] osgi-project - wrapper instructions
[INFO] osgi-project - bundle instructions
[INFO] osgi-project - imported bundles
[INFO] ------------------------------------------------------------------------
[INFO] Building org.sonatype.mcookbook.osgi-project (OSGi project)
[INFO] task-segment: [pax:import-bundle] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] [pax:import-bundle]
[INFO] Importing Apache Felix Web Management Console to \
org.sonatype.mcookbook.osgi-project.build:provision:pom:1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6 seconds
[INFO] Finished at: Mon Jul 13 18:30:37 CDT 2009
[INFO] Final Memory: 8M/16M
[INFO] ------------------------------------------------------------------------
After installing the Apache Felix Web Management console, you will need to install some of its prerequisites. The following commands install version 2.1 of the Java Servlet API, the Jetty HTTP server, and the org.apache.felix.scr component. As you can see, some of the OSGi bundle you are installing are simple libraries and APIs, while others are complex servers.
Once you have bundled these OSGi bundles, you can start your runtme environment. Because the pax:import-bundle goal changes some of the POMs in your osgi-project, you will need to run mvn install before you can run mvn pax:provision. After importing bundles, they will appear in provision/pom.xml.
[INFO] [pax:provision]
[INFO] Installing ~/examples/osgi/osgi-project/runner/target/pom-transformed.xml
to ~/.m2/repository/org/sonatype/mcookbook/osgi-project/build/deployment/1.0-SNAPSHOT/\ deployment-1.0-SNAPSHOT.pom
Pax Runner (1.0.0) from OPS4J - http://www.ops4j.org
----------------------------------------------------
-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [~/examples/osgi/osgi-project/runner/deploy-pom.xml]
-> Scan bundles from [scan-pom:file:~/examples/osgi/osgi-project/runner/deploy-pom.xml]
-> Provision bundle [mvn:org.apache.felix/org.apache.felix.webconsole/1.2.8, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.apache.felix/javax.servlet/1.0.0, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.apache.felix/org.apache.felix.http.jetty/1.0.1, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.apache.felix/org.apache.felix.scr/1.0.8, at default start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Felix 1.8.0]
-> Downloading bundles...
-> mvn:org.apache.felix/org.apache.felix.webconsole/1.2.8 : 102399 bytes
-> mvn:org.apache.felix/javax.servlet/1.0.0 : 30450 bytes @ [ 10150kBps ]
-> mvn:org.apache.felix/org.apache.felix.http.jetty/1.0.1 : 102399 bytes @
-> mvn:org.apache.felix/org.apache.felix.scr/1.0.8 : 112058 bytes @ [ 8619kBps ]
-> Using execution environment [J2SE-1.5]
-> Runner has successfully finished his job!
Welcome to Felix.
=================
-> org.mortbay.log:Logging to org.mortbay.log via org.apache.felix.http.jetty.LogServiceLog
org.mortbay.log:Init SecureRandom.
org.mortbay.log:started org.mortbay.jetty.servlet.HashSessionIdManager@34a33d
org.mortbay.log:started org.mortbay.jetty.servlet.HashSessionManager@46ac5a
org.mortbay.log:starting OsgiServletHandler@dd7786
org.mortbay.log:started OsgiServletHandler@dd7786
org.mortbay.log:starting SessionHandler@d23e75
org.mortbay.log:started SessionHandler@d23e75
org.mortbay.log:starting org.mortbay.jetty.servlet.Context@9deddb{/,null}
org.mortbay.log:starting ErrorHandler@28bda
org.mortbay.log:started ErrorHandler@28bda
org.mortbay.log:started org.mortbay.jetty.servlet.Context@9deddb{/,null}
org.mortbay.log:jetty-6.1.x
org.mortbay.log:started Realm[OSGi HTTP Service Realm]==[]
org.mortbay.log:started org.mortbay.thread.QueuedThreadPool@f13b08
org.mortbay.log:starting Server@5a936b
org.mortbay.log:started org.mortbay.jetty.nio.SelectChannelConnector$1@9576c3
org.mortbay.log:Started SelectChannelConnector@0.0.0.0:8080
org.mortbay.log:started SelectChannelConnector@0.0.0.0:8080
org.mortbay.log:started Server@5a936b
org.mortbay.log:started /system/console/*
org.mortbay.log:started /system/console/res
After running mvn pax:provision, you have an instance of Felix that is running the Apache Felix Web Management Console application. Fire up a web brower, and go to https://mvnrepository.com/artifact/org.apache.felix/org.apache.felix.webconsole to load the interface. The default administrative username and password is admin/admin.

Figure 1.2 Apache Felix Web Management Console
NOTE
Instead of executing these goals manually, you can also download and install the Pax-Construct scripts which can be used to automate the process of importing bundles with the Pax plugin. For more information, see the Pax Construct Quickstart page.
1.4.3 Detail
Once you start the web management console for Apache Felix, you can start to manage the installed bundles. You can start and stop bundles via the interface, and you can install bundles from remote repositories. If you prefer to use the shell interface from the previous recipe, click on the Shell interface and you can enter in any of the commands you used at the command-line Felix management console as shown in Figure 1.3, “Running the Apache Felix Shell via the Administrative Web Console”.
Figure 1.3 Running the Apache Felix Shell via the Administration Web Console
You can manage Felix interactions with events and repositories, and you can also restart, stop, and change the default run levels. Clicking on the "System Information" tab will also allow you to get some statistics about the runtime container as shown in Figure 1.4, “Managing the Apache Felix via System Information”.
Figure 1.4 Managing the Apache Felix via System Information
When we imported these bundles into the project, they were added to the provision/pom.xml file, and we had to install all of a bundle's dependencies one by one. Instead of listing out each of bundle's dependencies on the command-line, we can tell the Pax plugin to import transitive bundle dependencies and optional bundles. The following command line will instruct the Pax plugin to install a bundle's full compile and runtime dependencies in provision/pom.xml.
-DgroupId=org.apache.felix \
-DartifactId=org.apache.felix.http.jetty \
-Dversion=1.0.1 \
-DimportTransitive=true \
-DimportOptional=true \
-DwidenScope=true
The importTransitive and importOptional options tell the Pax plugin to look at the OBR metadata to find bundle dependencies. The widenScope parameter tells the Pax plugin to install all compile and runtime dependencies as bundles in provision/pom.xml.
1.4.4. Resources
For more information about the Apache Felix Web Console, see http://cwiki.apache.org/confluence/display/FELIX/Apache+Felix+Web+Console.
1.5. Creating an OSGi Bundle with Maven
1.5.1 Task
You need to create you own OSGi bundle project to integrate with the runtime environment created in the previous recipes.
1.5.2 Action
Run the pax:create-bundle goal. The following screen listing runs the pax:create-bundle to create a new Maven project for an OSGi bundle with a package org.sonatype.mcookbook and a name osgi-bundle. Run this command from the ~/examples/osgi/osgi-project directory:
-Dpackage=org.sonatype.mcookbook \
-Dname=osgi-bundle \
-Dversion=1.0-SNAPSHOT
Once this command has been completed, run mvn install pax:provision and then verify that the component is present by running ps at the Felix administrative console:
...
[INFO] Reactor build order:
[INFO] org.sonatype.mcookbook.osgi-project (OSGi project)
[INFO] osgi-project - plugin configuration
[INFO] osgi-project - wrapper instructions
[INFO] osgi-project - bundle instructions
[INFO] osgi-project - imported bundles
[INFO] org.sonatype.mcookbook
..
[INFO] ------------------------------------------------------------------------
[INFO] Building org.sonatype.mcookbook
[INFO] task-segment: [install]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources]
[WARNING] Using platform encoding (MacRoman actually) to copy filtered resources, i.e. build is platform \ dependent!
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO] [pax:compile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [pax:testCompile]
[INFO] No sources to compile
...
[INFO] [install:install]
[INFO] Installing ~/examples/osgi/osgi-project/org.sonatype.mcookbook/target/\
org.sonatype.mcookbook-1.0-SNAPSHOT.jar to \ ~/.m2/repository/.m2/repository/org/sonatype/mcookbook/osgi-project/org.sonatype.mcookbook/\ 1.0-SNAPSHOT/org.sonatype.mcookbook-1.0-SNAPSHOT.jar
[INFO] [bundle:install]
[INFO] Parsing file:~/.m2/repository/.m2/repository/repository.xml
[INFO] Installing org/sonatype/mcookbook/osgi-project/org.sonatype.mcookbook/1.0-SNAPSHOT/\ org.sonatype.mcookbook-1.0-SNAPSHOT.jar
[INFO] Writing OBR metadata
[INFO] ------------------------------------------------------------------------
[INFO] Building org.sonatype.mcookbook.osgi-project (OSGi project)
[INFO] task-segment: [pax:provision] (aggregator-style)
[INFO] ------------------------------------------------------------------------
The Pax plugin bundles the new OSGi bundle into a JAR file which is deployed to the local Maven repository in ~/.m2/repository. The resulting artifact has the following identifiers:
- groupId: org.sonatype.mcookbook.osgi-project
- artifactId: org.sonatype.mcookbook
- version: 1.0-SNAPSHOT
Continuing on with this particular execution of Maven, lines corresponding to this new custom OSGi bundle have been highlighted. The mvn:org.sonatype.mcookbook.osgi-project/org.sonatype.mcookbook/1.0-SNAPSHOT is provisioned to Apache Felix and after running the ps command we see that the org.sonatype.mcookbook bundle is deployed with ID 5.
[INFO] Installing ~/examples/osgi/osgi-project/runner/target/pom-transformed.xml to \ ~/.m2/repository/.m2/repository/org/sonatype/mcookbook/osgi-project/build/deployment/1.0-SNAPSHOT/\ deployment-1.0-SNAPSHOT.pom
Pax Runner (1.0.0) from OPS4J - http://www.ops4j.org
----------------------------------------------------
-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [~/examples/osgi/osgi-project/runner/deploy-pom.xml]
-> Scan bundles from [scan-pom:file:~/examples/osgi/osgi-project/runner/deploy-pom.xml]
-> Provision bundle [mvn:org.apache.felix/org.apache.felix.webconsole/1.2.8, at default start level, \ bundle will be started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.apache.felix/javax.servlet/1.0.0, at default start level, bundle will be \ started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.apache.felix/org.apache.felix.scr/1.0.8, at default start level, bundle will be \ started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.apache.felix/org.apache.felix.http.jetty/1.0.1, at default start level, bundle \ will be started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.sonatype.mcookbook.osgi-project/org.sonatype.mcookbook/1.0-SNAPSHOT, at default \ start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Felix 1.8.0]
...
-> Runner has successfully finished his job!
Welcome to Felix.
=================
-> org.mortbay.log:Logging to org.mortbay.log via org.apache.felix.http.jetty.LogServiceLog
STARTING org.sonatype.mcookbook
REGISTER org.sonatype.mcookbook.ExampleService
org.mortbay.log:Init SecureRandom.
...
org.mortbay.log:started /system/console/res
-> ps
START LEVEL 6
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.8.0)
[ 1] [Active ] [ 5] Apache Felix Web Management Console (1.2.8)
[ 2] [Active ] [ 5] Servlet 2.1 API (1.0.0)
[ 3] [Active ] [ 5] Apache Felix Declarative Services (1.0.8)
[ 4] [Active ] [ 5] HTTP Service (1.0.1)
[ 5] [Active ] [ 5] org.sonatype.mcookbook (1.0.0.SNAPSHOT)
[ 6] [Active ] [ 1] osgi.compendium (4.1.0.build-200702212030)
[ 7] [Active ] [ 1] Apache Felix Shell Service (1.2.0)
[ 8] [Active ] [ 1] Apache Felix Shell TUI (1.2.0)
NOTE
Instead of executing these goals manually, you can also download and install the Pax-Construct scripts. For more information, see the Pax Construct Quickstart page.
1.5.3 Detail
So you just deployed your own OSGi bundle to a OSGi runtime environment, but where is the code? And, what is in this OSGi bundle anyway? Open up your osgi-project directory and you'll see a directory named org.sonatype.mcookbook/ with a directory structure similar to that shown in Figure 1.5, “Custom Bundle org.sonatype.mcookbook
Figure 1.5. Custom Bundle org.sonatype.mcookbook
This sample project defines a single interface named ExampleService which offers a single method scramble().
Example 1.1. ExampleService Interface
/**
* Public API representing an example OSGi service
*/
public interface ExampleService
{
// public methods go here...
String scramble( String text );
}
This ExampleService interface is implemented by the class ExampleServiceImpl shown in the following program listing.
Example 1.2. ExampleServiceImpl Implementation of the ExampleService Interface
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.sonatype.mcookbook.ExampleService;
/**
* Internal implementation of our example OSGi service
*/
public final class ExampleServiceImpl
implements ExampleService
{
// implementation methods go here...
public String scramble( String text )
{
List charList = new ArrayList();
char[] textChars = text.toCharArray(); for( int i = 0; i < textChars.length; i++ )
{
charList.add( new Character( textChars[i] ) );
}
Collections.shuffle( charList );
char[] mixedChars = new char[text.length()];
for( int i = 0; i < mixedChars.length; i++ )
{
mixedChars[i] = ( (Character) charList.get( i ) ).charValue();
}
return new String( mixedChars );
}
}
Notice how neither ExampleService nor ExampleServiceImpl have any knowledge of the OSGi environment. For the ExampleService to be made available to the OSGi environment, there needs to be a BundleActivator which can instantiate the service and interact with the OSGi environment. This is included in the project in the form of an ExampleActivator which has a method named start() which takes a BundleContext instance and which calls registerService() on this context with an instance of ExampleServiceImpl.
Example 1.3. The BundleActivator implementation: ExampleActivator
import java.util.Dictionary;
import java.util.Properties;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.sonatype.mcookbook.ExampleService;
/**
* Extension of the default OSGi bundle activator
*/
public final class ExampleActivator
implements BundleActivator
{
/**
* Called whenever the OSGi framework starts our bundle
*/
public void start( BundleContext bc )
throws Exception
{
System.out.println( "STARTING org.sonatype.mcookbook" );
Dictionary props = new Properties();
// add specific service properties here...
System.out.println( "REGISTER org.sonatype.mcookbook.ExampleService" );
// Register our example service implementation in the OSGi service registry
bc.registerService( ExampleService.class.getName(), new ExampleServiceImpl(), props );
}
/**
* Called whenever the OSGi framework stops our bundle
*/
public void stop( BundleContext bc )
throws Exception
{
System.out.println( "STOPPING org.sonatype.mcookbook" );
// no need to unregister our service - the OSGi framework handles it for us
}
}
Lastly, the osgi.bnd file in ~/examples/osgi/osgi-project/org.sonatype.mcookbook supplies the bundle configuration which identifies the ExampleActivator class as the appropriate Bundle-Activator.
Example 1.4. osgi.bnd Bundle Configuration
# Use this file to add customized Bnd instructions for the bundle
#-----------------------------------------------------------------
Bundle-Activator: ${bundle.namespace}.internal.ExampleActivator
1.6. Starting an Alternative OSGi Framework (Knopflerfish)
1.6.1. Task
You want to run your OSGi bundles in the Knopflerfish OSGi runtime environment.
1.6.2. Action
Pass in the framework configuration parameter when running the pax:provision goal. To start Knopflerfish, run mvn pax:provision -Dframework=kf.
[INFO] Scanning for projects...
...
[INFO] [pax:provision]
[INFO] Installing ~/examples/osgi/osgi-project/runner/target/pom-transformed.xml to \
/Users/Tim/.m2/repository/org/sonatype/mcookbook/osgi-project/build/deployment/1.0-SNAPSHOT/\ deployment-1.0-SNAPSHOT.pom
[INFO] artifact org.ops4j.pax.runner:pax-runner: checking for updates from central Downloading: http://repo1.maven.org/maven2/org/ops4j/pax/runner/pax-runner/1.1.1/pax-runner-1.1.1.jar 1067K downloaded (pax-runner-1.1.1.jar)
Pax Runner (1.1.1) from OPS4J - http://www.ops4j.org
----------------------------------------------------
-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [~/examples/osgi/osgi-project/runner/deploy-pom.xml]
-> Scan bundles from [scan-pom:file:~/examples/osgi/osgi-project/runner/deploy-pom.xml]
...
-> Preparing framework [Knopflerfish 2.3.1]
-> Downloading bundles...
-> Knopflerfish 2.3.1 : 366660 bytes @ [ 160kBps ]
-> org.osgi.compendium : 689150 bytes @ [ 256kBps ]
-> Knopflerfish Console : 36329 bytes @ [ 55kBps ]
-> Knopflerfish Console TTY : 6153 bytes @ [ 1538kBps ]
-> Knopflerfish Framework Commands : 26090 bytes @ [ 158kBps ]
-> Using execution environment [J2SE-1.5]
-> Runner has successfully finished his job!
Knopflerfish OSGi framework, version 4.1.3
Copyright 2003-2009 Knopflerfish. All Rights Reserved.
See http://www.knopflerfish.org for more information.
Loading xargs url file:knopflerfish/config.ini
Installed and started: file:bundles/osgi.compendium_4.0.1.jar (id#1)
Installed and started: file:bundles/org.knopflerfish.bundle.console_2.0.1.jar (id#2)
Installed and started: file:bundles/org.knopflerfish.bundle.consoletty-IMPL_2.0.0.jar (id#3)
Installed and started: file:bundles/org.knopflerfish.bundle.frameworkcommands-IMPL_2.0.5.jar (id#4)
Installed and started: file:bundles/org.apache.felix.webconsole_1.2.8.jar (id#5)
Installed and started: file:bundles/org.apache.felix.javax.servlet_1.0.0.jar (id#6)
Installed and started: file:bundles/org.apache.felix.scr_1.0.8.jar (id#7)
Installed and started: file:bundles/org.apache.felix.http.jetty_1.0.1.jar (id#8)
Installed and started: file:bundles/org.sonatype.mcookbook_1.0.0.SNAPSHOT.jar (id#9)
> STARTING org.sonatype.mcookbook
REGISTER org.sonatype.mcookbook.ExampleService
org.mortbay.log:Logging to org.mortbay.log via org.apache.felix.http.jetty.LogServiceLog Framework launched
org.mortbay.log:Init SecureRandom.
org.mortbay.log:started org.mortbay.jetty.servlet.HashSessionIdManager@b890dc
org.mortbay.log:started org.mortbay.jetty.servlet.HashSessionManager@6321e6
org.mortbay.log:starting OsgiServletHandler@4683c0
org.mortbay.log:started OsgiServletHandler@4683c0
org.mortbay.log:starting SessionHandler@73bc22
org.mortbay.log:started SessionHandler@73bc22
org.mortbay.log:starting org.mortbay.jetty.servlet.Context@a0d346{/,null}
org.mortbay.log:starting ErrorHandler@449afc
org.mortbay.log:started ErrorHandler@449afc
org.mortbay.log:started org.mortbay.jetty.servlet.Context@a0d346{/,null}
org.mortbay.log:jetty-6.1.x
org.mortbay.log:started Realm[OSGi HTTP Service Realm]==[]
org.mortbay.log:started org.mortbay.thread.QueuedThreadPool@39491b
org.mortbay.log:starting Server@eea7f0
org.mortbay.log:started org.mortbay.jetty.nio.SelectChannelConnector$1@31db04
org.mortbay.log:Started SelectChannelConnector@0.0.0.0:8080
org.mortbay.log:started SelectChannelConnector@0.0.0.0:8080
org.mortbay.log:started Server@eea7f0
org.mortbay.log:started /system/console/*
org.mortbay.log:started /system/console/res
The output of this last command is going to be somewhat confusing. Even though we're starting the application using Knopflerfish, we're still deploying some OSGi components from Apache Felix, and we're deploying the Apache Felix Web Management Console. The interesting thing about OSGi: it all still works. The Apache Felix Web Management Console can be used to manage standard OSGi component even when it is running in the Knopflerfish container. To test this, go to http://localhost:8080/system/console, and log in with the default admin/admin credentials. Click on the configuration status tab, and then scroll down to see properties that prove that your Apache Felix web management console is running atop Knopflerfish.
1.6.3. Detail
For more information about Knopflerfish, see http://www.knopflerfish.org. For more information about the frameworks available to the Pax plugin, see https://ops4j1.jira.com/wiki/spaces/paxrunner/overview.
1.7. Starting an Alternative OSGi Framework (Equinox)
1.7.1. Task
You want to run your OSGi bundles in Eclipse's Equinox OSGi runtime environment.
1.7.2. Action
Pass in the framework configuration parameter when running the pax:provision goal. To start Knopflerfish, run mvn pax:provision -Dframework=equinox.
[INFO] Scanning for projects...
[INFO] Reactor build order:
[INFO] org.sonatype.mcookbook.osgi-project (OSGi project)
[INFO] osgi-project - plugin configuration
[INFO] osgi-project - wrapper instructions
[INFO] osgi-project - bundle instructions
[INFO] osgi-project - imported bundles
[INFO] org.sonatype.mcookbook
[INFO] ------------------------------------------------------------------------
[INFO] Building org.sonatype.mcookbook.osgi-project (OSGi project)
[INFO] task-segment: [pax:provision] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] [pax:provision]
[INFO] Installing /Users/Tim/Library/Code/sonatype/maven-cookbook/mcookbook-examples/osgi/osgi-project/\ runner/target/pom-transformed.xml to \ /Users/Tim/.m2/repository/org/sonatype/mcookbook/osgi-project/build/deployment/1.0-SNAPSHOT/\ deployment-1.0-SNAPSHOT.pom
Pax Runner (1.1.1) from OPS4J - http://www.ops4j.org
----------------------------------------------------
-> Using config [classpath:META-INF/runner.properties]
-> Using only arguments from command line
-> Scan bundles from [/Users/Tim/Library/Code/sonatype/maven-cookbook/mcookbook-examples/osgi/osgi-project/\ runner/deploy-pom.xml]
-> Scan bundles from [scan-pom:file:/Users/Tim/Library/Code/sonatype/maven-cookbook/mcookbook-examples/osgi/\ osgi-project/runner/deploy-pom.xml]
-> Provision bundle [mvn:org.apache.felix/org.apache.felix.webconsole/1.2.8, at default start level, bundle \ will be started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.apache.felix/javax.servlet/1.0.0, at default start level, bundle will be \ started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.apache.felix/org.apache.felix.scr/1.0.8, at default start level, bundle will be \ started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.apache.felix/org.apache.felix.http.jetty/1.0.1, at default start level, bundle \ will be started, bundle will be loaded from the cache]
-> Provision bundle [mvn:org.sonatype.mcookbook.osgi-project/org.sonatype.mcookbook/1.0-SNAPSHOT, at default \ start level, bundle will be started, bundle will be loaded from the cache]
-> Preparing framework [Equinox 3.5.0]
-> Downloading bundles...
-> Equinox 3.5.0 : 1122602 bytes @ [ 300kBps ]
-> Eclipse utilities : 22471 bytes @ [ 64kBps ]
-> Eclipse compendium services : 66065 bytes @ [ 113kBps ]
-> Using execution environment [J2SE-1.5]
-> Runner has successfully finished his job!
osgi> STARTING org.sonatype.mcookbook
REGISTER org.sonatype.mcookbook.ExampleService
org.mortbay.log:Logging to org.mortbay.log via org.apache.felix.http.jetty.LogServiceLog
org.mortbay.log:Init SecureRandom.
org.mortbay.log:started org.mortbay.jetty.servlet.HashSessionIdManager@9ed5d6
org.mortbay.log:started org.mortbay.jetty.servlet.HashSessionManager@fd245f
org.mortbay.log:starting OsgiServletHandler@5f00f9
org.mortbay.log:started OsgiServletHandler@5f00f9
org.mortbay.log:starting SessionHandler@38ef1d
org.mortbay.log:started SessionHandler@38ef1d
org.mortbay.log:starting org.mortbay.jetty.servlet.Context@cc3baa{/,null}
org.mortbay.log:starting ErrorHandler@da6d09
org.mortbay.log:started ErrorHandler@da6d09
org.mortbay.log:started org.mortbay.jetty.servlet.Context@cc3baa{/,null}
org.mortbay.log:jetty-6.1.x
org.mortbay.log:started Realm[OSGi HTTP Service Realm]==[]
org.mortbay.log:started org.mortbay.thread.QueuedThreadPool@f2e41d
org.mortbay.log:starting Server@5e4dbe
org.mortbay.log:started org.mortbay.jetty.nio.SelectChannelConnector$1@9a44d6
org.mortbay.log:Started SelectChannelConnector@0.0.0.0:8080
org.mortbay.log:started SelectChannelConnector@0.0.0.0:8080
org.mortbay.log:started Server@5e4dbe
org.mortbay.log:started /system/console/*
org.mortbay.log:started /system/console/res
Again, the output of this last command is going to be somewhat confusing. Even though we're starting the application using Equinox, we're still deploying some OSGi components from Apache Felix, and we're deploying the Apache Felix Web Management Console. While the Felix Web Management Console can also run in the Equinox OSGi runtime environment, take note that there are some missing classes toward the end of the previous console output. This is likely due to the fact that there is a missing bundle or some Felix-specific functionality that is present in the Felix management console. To test the console, go to https://mvnrepository.com/artifact/org.apache.felix/org.apache.felix.webconsole, and log in with the default admin/admin credentials.
1.7.3. Detail
For more information about Equinox, see https://equinox.eclipseprojects.io/. For more information about the frameworks available to the Pax plugin, see https://ops4j1.jira.com/wiki/spaces/paxrunner/overview.
1.8. Deploying OSGi Bundles to a Maven Repository
1.8.1. Task
You need to configure your project to deploy your own custom OSGi bundles to a Maven repository.
1.8.2. Action
The solution is to download an install Sonatype Nexus Repository, and configure your Maven environment to deploy snapshot and release artifacts to a Hosted Maven repository. To install Sonatype Nexus Repository, complete the following steps:
- Download Sonatype Nexus Repository from https://www.sonatype.com/products/sonatype-nexus-pro/trial.
- Unpack the Sonatype Nexus Repository archive on your local workstation, or on the machine that will host Sonatype Nexus Repository for your workgroup or organization.
- Start Sonatype Nexus Repository by running the startup script in ${nexus_install_dir}/bin/jsw/<platform>/nexus start where <platform> is replaced with the appropriate platform-specific directory.
The following commands will install and start Sonatype Nexus Repository 1.3.5:
~/programs $ tar xvzf nexus-professional-webapp-1.3.5-bundle.tar.gz
~/programs $ cd nexus-professional-webapp-1.3.5
~/programs/nexus-professional-webapp-1.3.5 $ ./bin/jsw/macosx-universal-32/nexus start
Starting Sonatype Nexus Repository Manager...
Started Sonatype Nexus Repository Manager.
Create a ~/.m2/settings.xml and copy the XML from Example 1.5, “settings.xml to configure deployment credentials and mirrors for Nexus” into this new Maven Settings XML file.
Example 1.5. settings.xml to configure deployment credentials and mirrors for Nexus
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://localhost:8081/nexus/content/groups/public</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>nexus</id>
<repositories>
<repository>
<id>central</id>
<url>http://central</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://central</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<servers>
<server>
<id>nx-snapshots</id>
<username>deployment</username>
<password>deployment123</password>
</server>
<server>
<id>nx-releases</id>
<username>deployment</username>
<password>deployment123</password>
</server>
</servers>
<activeProfiles>
<activeProfile>nexus</activeProfile>
</activeProfiles>
</settings>
Now, go into the osgi-project example project from the previous labs and add the XML from Example 1.6, “Distribution Management Settings for the osgi-project Project” into the top-level parent in ~/examples/osgi/osgi-project/pom.xml. You can add the distributionManagement element as a child of the project element after all of the existing children.
Example 1.6. Distribution Management Settings for the osgi-project Project
<project> ... <distributionManagement> <repository> <id>nx-releases</id> <name>Nexus Releases</name> <url>http://localhost:8081/nexus/content/repositories/releases</url> </repository> <snapshotRepository> <id>nx-snapshots</id> <name>Nexus Snapshots</name> <url>http://localhost:8081/nexus/content/repositories/snapshots</url> </snapshotRepository> </distributionManagement> </project>
Now, deploy the bundle from your osgi-project to the Nexus Snapshots repository by running mvn deploy -DremoteOBR.
~/osgi/osgi-project $ mvn deploy -DremoteOBR
[INFO] Scanning for projects...
...
[INFO] [deploy:deploy]
[INFO] Retrieving previous build number from nx-snapshots
Uploading: http://localhost:8081/nexus/content/repositories/snapshots/org/sonatype/mcookbook/osgi-project/\
1.0-SNAPSHOT/osgi-project-1.0-20090715.030835-2.pom
2K uploaded (osgi-project-1.0-20090715.030835-2.pom)
[INFO] Retrieving previous metadata from nx-snapshots
[INFO] Uploading repository metadata for: 'artifact org.sonatype.mcookbook:osgi-project'
[INFO] Retrieving previous metadata from nx-snapshots
[INFO] Uploading repository metadata for: 'snapshot org.sonatype.mcookbook:osgi-project:1.0-SNAPSHOT'
...
[INFO] [deploy:deploy]
[INFO] Retrieving previous build number from nx-snapshots
Uploading: http://localhost:8081/nexus/content/repositories/snapshots/org/sonatype/mcookbook/osgi-project/\
build/shared-plugin-settings/1.0-SNAPSHOT/shared-plugin-settings-1.0-20090715.030835-2.pom
2K uploaded (shared-plugin-settings-1.0-20090715.030835-2.pom)
[INFO] Retrieving previous metadata from nx-snapshots
[INFO] Uploading repository metadata for: 'artifact \
org.sonatype.mcookbook.osgi-project.build:shared-plugin-settings'
[INFO] Retrieving previous metadata from nx-snapshots
[INFO] Uploading repository metadata for: 'snapshot \
org.sonatype.mcookbook.osgi-project.build:shared-plugin-settings:1.0-SNAPSHOT'
...
[INFO] [deploy:deploy]
[INFO] Retrieving previous build number from nx-snapshots
Uploading: http://localhost:8081/nexus/content/repositories/snapshots/org/sonatype/mcookbook/osgi-project/\
build/wrapper-bundle-settings/1.0-SNAPSHOT/wrapper-bundle-settings-1.0-20090715.030835-2.pom
1K uploaded (wrapper-bundle-settings-1.0-20090715.030835-2.pom)
[INFO] Retrieving previous metadata from nx-snapshots
[INFO] Uploading repository metadata for: 'artifact \
org.sonatype.mcookbook.osgi-project.build:wrapper-bundle-settings'
[INFO] Retrieving previous metadata from nx-snapshots
[INFO] Uploading repository metadata for: 'snapshot \
org.sonatype.mcookbook.osgi-project.build:wrapper-bundle-settings:1.0-SNAPSHOT'
...
[INFO] [deploy:deploy]
[INFO] Retrieving previous build number from nx-snapshots
...
[INFO] [bundle:deploy]
[INFO] LOCK http://localhost:8081/nexus/content/repositories/snapshots/repository.xml
[INFO] Downloading repository.xml
[INFO] Parsing file:/var/folders/qR/qRpEDfQcFPmRZpsXgPHOok+++TM/-Tmp-/12476273209637081490707781551794.xml
[INFO] Deploying org/sonatype/mcookbook/osgi-project/org.sonatype.mcookbook/1.0-SNAPSHOT/\
org.sonatype.mcookbook-1.0-20090715.030835-2.jar
[INFO] Writing OBR metadata
[INFO] Uploading repository.xml
[INFO] UNLOCK http://localhost:8081/nexus/content/repositories/snapshots/repository.xml
To summarize, you downloaded and installed Nexus Professional, configured your Maven Settings to supply the default credentials for the deployment user, you added the snapshots and releases repositories to your project's pom.xml, and then you deployed your project's SNAPSHOT artifacts to he snapshot repository and updated an OBR-compatible repository.xml file also stored in the Nexus-hosted Maven repository.
Warning
We skipped some extremely important setup tasks for Nexus Professional, namely changing all of the default passwords. This example uses the default username and password for the deployment user (deployment/deployment123). To change this password, log into the Nexus interface by going to http://localhost:8081/nexus, logging in with the default administrative credentials (admin/admin123), clicking on Users under Security in the left navigation menu, and then right-clicking on the deployment user in the list of users. Once you right-click on the deployment user, select "Set Password" and then supply a new password. Don't leave Nexus unprotected with the default username and password.
1.8.3. Detail
To verify that your artifacts made it into the Snapshots repository on your local Nexus instance, go to the Nexus interface at http://localhost:8081/nexus and login as the administrative user. The default administrative credentials are the username "admin" and the password "admin123". Once you login as the admin user, click on Repositories in the left navigation menu, and then select the Snapshots repository. If you browse the contents of the repository you should see contents similar to the contents shown in Figure 1.6, “The Contents of the Snapshots Repository after Deployment”.
Figure 1.6. The Contents of the Snapshots Repository after Deployment
You can see from Figure 1.6, “The Contents of the Snapshots Repository after Deployment” that the repository contains the org.sonatype.mcookbook OSGi bundle under the org.sonatype.mcookbook.osgi-project groupId and that the Pax plugin took care of creating an OSGi bundle repository XML file at repository.xml.
If you want to test how easy it would be to install this component directly from Nexus:
-
Download Apache Felix from http://www.apache.org/dist/felix/felix-1.8.0.tar.gz.
-
Unpack the Felix distribution on your local workstation.
-
Change directory to the Felix directory.
-
Start Apache Felix with java -jar ./bin/felix.jar
-
Add the Snapshots repository as an OBR repository by copying the URL to the repository.xml and passing it to the obr command: obr add-url http://localhost:8081/nexus/content/repositories/snapshots/repository.xml
-
List the contents of the OBR repository.
-
Deploy the org.sonatype.mcookbook bundle.
-
List the installed bundles.
These commands are captured in the following screen listing:
~/programs $ wget http://www.apache.org/dist/felix/felix-1.8.0.tar.gz --2009-07-14 22:46:19-- http://www.apache.org/dist/felix/felix-1.8.0.tar.gz Resolving www.apache.org... 140.211.11.130 Connecting to www.apache.org|140.211.11.130|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 901620 (880K) [application/x-gzip] Saving to: `felix-1.8.0.tar.gz' 2009-07-14 22:46:20 (924 KB/s) - `felix-1.8.0.tar.gz' saved [901620/901620] ~/programs $ tar xvzf felix-1.8.0.tar.gz felix-1.8.0/ felix-1.8.0/bin/ felix-1.8.0/bin/felix.jar ... felix-1.8.0/NOTICE ~/programs $ cd felix-1.8.0 ~/programs/felix-1.8.0 $ java -jar ./bin/felix.jar Welcome to Felix. ================= -> obr add-url http://localhost:8081/nexus/content/repositories/snapshots/repository.xml -> obr list org.sonatype.mcookbook (1.0.0.SNAPSHOT) -> obr deploy org.sonatype.mcookbook Target resource(s): ------------------- org.sonatype.mcookbook (1.0.0.SNAPSHOT) Deploying...done. -> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (1.8.0) [ 1] [Active ] [ 1] Apache Felix Shell Service (1.2.0) [ 2] [Active ] [ 1] Apache Felix Shell TUI (1.2.0) [ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0) [ 4] [Installed ] [ 1] org.sonatype.mcookbook (1.0.0.SNAPSHOT) -> start 4 STARTING org.sonatype.mcookbook REGISTER org.sonatype.mcookbook.ExampleService ->
In summary, you've deployed an OSGi bundle to a Maven repository using the Pax plugin from OPS4J and you've successfully deployed it to an independent instance of Apache Felix directly from your Nexus instance. This is a powerful demonstration because it provides a model for how deployment to production of staging can work once you've standardized on a Maven repository manager and OSGi as a deployment framework.
Note
You can also setup and install Nexus Open Source, and rely on the first-class support that the the Pax plugin provides for OSGi bundles stored in a standard Maven repository. In the context of OSGi development, the main difference between Nexus Professional and Nexus Open Source is that Nexus Professional provides direct support for OBR repositories, allowing you to proxy, host, and group OSGi bundle repositories and convert existing Maven repositories to OSGi bundle repositories.
1.9. Transforming a Maven Repository into an OSGi Bundle Repository
In the previous recipe, you published an OSGi bundle to a Maven repository, and you relied on the Pax plugin to read and write the repository.xml file. While this approximates an OBR repository, you'd like Nexus to take care of generating the OBR metadata from the repository contents, and you don't want to rely on individual builds to lock, read, parse, alter, write, and unlock a shared OBR metadata file. Instead of relying on the Pax plugin, this recipe shows you how to configure an OBR repository in Sonatype Nexus Repository.
1.9.1. Task
You need to expose OSGi bundles in a Maven repository using the OBR repository format.
1.9.2. Action
Use Sonatype Nexus Repository, and create a virtual OBR repository which will scan a Maven repository for OSGi bundle artifacts and generate the OBR XML metadata in response to a change in the Maven repository. Building upon the example from the previous recipe, create a Virtual OBR repository named Snapshots OBR which uses the Snapshots repository as a source. To do this:
- Load the Nexus interface in a web browser by opening the URL http://localhost:8081/nexus.
- Login as an administrative user using the default credentials of admin/admin123 (if you haven't already changed the default password).
- Click on Repositories in the left navigation menu.
- Click on the Add.. button above the list of Nexus repositories and groups.
- Select "Virtual Repository" from the resulting dropdown.
- In the New Virtual Repository window, supply the following values as shown in Figure 1.7, “Creating a New Virtual OBR Repository”:
- Repository ID: snapshots-obr
- Repository Name: Snapshots OBR
- Provider: OBR
- Source Nexus Repository ID: snapshots
- Synchronize on Startup: False
- Click the Save button to create the new Virtual repository.
Figure 1.7. Creating a New Virtual OBR Repository
1.9.3. Detail
To verify that our new Virtual OBR repository is generating the the appropriate XML based on the contents of the Maven repository, click on Repositories in the left navigation menu, and then select "Snapshots OBR" from the list of repositories. If you look at the Browse tab for this repository and drill into the .meta/ directory, you can then right-click on the obr.xml file and select Download from the context menu. This should load the repository XML in a browser, and the contents of this XML file should resemble the XML file in Example 1.7, “Metadata for the Snapshots OBR Virtual Repository”. The URL for this new repository XML should be http://localhost:8081/nexus/content/repositories/snapshots-obr/.meta/obr.xml.
Example 1.7. Metadata for the Snapshots OBR Virtual Repository
<resource id='org.sonatype.mcookbook/1.0.0.SNAPSHOT' presentationname='org.sonatype.mcookbook' symbolicname='org.sonatype.mcookbook' uri='../org/sonatype/mcookbook/osgi-project/org.sonatype.mcookbook/1.0-SNAPSHOT/\ org.sonatype.mcookbook-1.0-20090715.030813-1.jar' version='1.0.0.SNAPSHOT'>
<description>
Generated using Pax-Construct
</description>
<size>
5345
</size>
<capability name='bundle'>
<p n='manifestversion' v='2'/>
<p n='presentationname' v='org.sonatype.mcookbook'/>
<p n='symbolicname' v='org.sonatype.mcookbook'/>
<p n='version' t='version' v='1.0.0.SNAPSHOT'/>
</capability>
<capability name='package'>
<p n='package' v='org.sonatype.mcookbook'/>
<p n='version' t='version' v='1.0.0.SNAPSHOT'/>
</capability>
<require extend='false' filter='(&(package=org.osgi.framework)(version>=0.0.0))' multiple='false' name='package' optional='false'>
Import package org.osgi.framework
</require>
<require extend='false' filter='(&(package=org.sonatype.mcookbook)(version>=1.0.0.SNAPSHOT))' multiple='false' name='package' optional='false'>
Import package org.sonatype.mcookbook ;version=1.0.0.SNAPSHOT
</require>
</resource>
<resource id='org.sonatype.mcookbook/1.0.0.SNAPSHOT' presentationname='org.sonatype.mcookbook' symbolicname='org.sonatype.mcookbook' uri='../org/sonatype/mcookbook/osgi-project/org.sonatype.mcookbook/1.0-SNAPSHOT/\ org.sonatype.mcookbook-1.0-20090715.030835-2.jar' version='1.0.0.SNAPSHOT'>
<description>
Generated using Pax-Construct
</description>
<size>
5346
</size>
<capability name='bundle'>
<p n='manifestversion' v='2'/>
<p n='presentationname' v='org.sonatype.mcookbook'/>
<p n='symbolicname' v='org.sonatype.mcookbook'/>
<p n='version' t='version' v='1.0.0.SNAPSHOT'/>
</capability>
<capability name='package'>
<p n='package' v='org.sonatype.mcookbook'/>
<p n='version' t='version' v='1.0.0.SNAPSHOT'/>
</capability>
<require extend='false' filter='(&(package=org.osgi.framework)(version>=0.0.0))' multiple='false' name='package' optional='false'> Import package org.osgi.framework </require>
<require extend='false' filter='(&(package=org.sonatype.mcookbook)(version>=1.0.0.SNAPSHOT))' multiple='false' name='package' optional='false'>
Import package org.sonatype.mcookbook ;version=1.0.0.SNAPSHOT
</require>
</resource>
</repository>
If you want to test how easy it would be to install this component directly from this new Nexus virtual OBR directory. If you've already installed Felix, from the previous recipe, delete the felix-1.8.0 directory and recreate the entire environment to take note of any changes in the behavior of Felix when using the new Snapshots OBR virtual repository:
- Download Apache Felix from https://felix.apache.org/documentation/downloads.html.
- Unpack the Felix distribution on your local workstation.
- Change directory to the Felix directory.
- Start Apache Felix with java -jar ./bin/felix.jar
- Add the Snapshots repository as an OBR repository by copying the URL to the repository.xml and passing it to the obr command: obr add-url http://localhost:8081/nexus/content/repositories/snapshots-obr/.meta/obr.xml
- List the contents of the OBR repository.
- Deploy the org.sonatype.mcookbook bundle.
- List the installed bundles.
These commands are captured in the following screen listing:
--2009-07-14 22:46:19-- http://www.apache.org/dist/felix/felix-1.8.0.tar.gz
Resolving www.apache.org... 140.211.11.130
Connecting to www.apache.org|140.211.11.130|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 901620 (880K) [application/x-gzip]
Saving to: `felix-1.8.0.tar.gz'
2009-07-14 22:46:20 (924 KB/s) - `felix-1.8.0.tar.gz' saved [901620/901620]
~/programs $ tar xvzf felix-1.8.0.tar.gz
felix-1.8.0/
felix-1.8.0/bin/
felix-1.8.0/bin/felix.jar
... felix-1.8.0/NOTICE
~/programs $ cd felix-1.8.0
~/programs/felix-1.8.0 $ java -jar ./bin/felix.jar
Welcome to Felix.
=================
-> obr add-url http://localhost:8081/nexus/content/repositories/snapshots-obr/.meta/obr.xml
-> obr list
org.sonatype.mcookbook (1.0.0.SNAPSHOT, ...)
-> obr deploy org.sonatype.mcookbook
Target resource(s):
-------------------
org.sonatype.mcookbook (1.0.0.SNAPSHOT)
Deploying...done.
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.8.0)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.2.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.2.0)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0)
[ 4] [Installed ] [ 1] org.sonatype.mcookbook (1.0.0.SNAPSHOT)
-> start 4
STARTING org.sonatype.mcookbook
REGISTER org.sonatype.mcookbook.ExampleService
->
Aside from the output of the obr list command, this process was exactly the same as the previous recipe. The main difference between this recipe and the previous recipe is that we do not rely on the Pax Plugin to generate the repository XML file that makes the repository an OBR repository. Instead, we rely upon the OBR integration of Sonatype Nexus Repository. Every time the underlying source repository (in this case the hosted snapshots repository) is changed, Sonatype Nexus Repository will regenerate the OBR XML in this virtual OBR repository. Using this virtual repository, you can simply deploy your project's OSGi and non-OSGi artifacts to the same Maven repository and then you can expose OSGi bundles to systems such as Felix using Sonatype Nexus Repository's support for virtual OBR repositories.
If you are using Sonatype Nexus Repository's OBR support and exposing a Maven repository as an OBR repository using a virtual repository, there is no need for you to pass the -DremoteOBR option to mvn deploy as we did in the previous recipe.
1.10. Proxying OSGi Bundle Repositories
If you've followed the recipes in this chapter sequentially, you deployed an artifact to a Maven repository and you've seen two ways to get that repository to an OSGi container: via a standard Maven repository and via a virtual OBR repository that acts as a transformer between Maven and OSGi. This chapter has focused on hosted repositories so far, but one of the most common reasons for people to use repository managers is to proxy remote repositories. In this recipe, you will learn how to proxy a remote OSGi bundle repository using Sonatype Nexus Repository's support for OBR.
1.10.1. Task
You would like to maintain a local cache of a remote OSGi Bundle repository that is populated with artifacts as they are requested. You need to do this for a few reasons, but mostly because you realized how inefficient it is for everyone in your organization to continuously query a remote server and download artifacts over the public internet. You are looking for more stability and control over what people are using from a remote OSGi bundle repository.
1.10.2. Action
Use Sonatype Nexus Repository to create a proxy repository for a remote OSGi bundle repository. To do this:
- Load the Nexus interface in a web browser by opening the URL http://localhost:8081/nexus.
- Login as an administrative user using the default credentials of admin/admin123 (if you haven't already changed the default password).
- Click on Repositories in the left navigation menu.
- Click on the Add.. button above the list of Nexus repositories and groups.
- Select "Proxy Repository" from the resulting dropdown.
- In the New Virtual Repository window, supply the following values as shown in Figure 1.8, “Creating a New Proxy Repository”:
- Repository ID: felix-obr
- Repository Name: Felix OBR
- Provider: OBR
- Remote Storage Location: http://felix.apache.org/obr/releases.xml
- Click the Save button to create the new Proxy repository.

Figure 1.8. Creating a New Proxy Repository
1.10.3. Detail
To verify that our new Proxy OBR repository is generating the the appropriate XML based on the remote OBR repository, click on Repositories in the left navigation menu, and then select "Felix OBR" from the list of repositories. If you look at the Browse tab for this repository and drill into the .meta/ directory, you can then right-click on the obr.xml file and select Download from the context menu. The URL for this new repository XML should be http://localhost:8081/nexus/content/repositories/felix-obr/.meta/obr.xml. Be warned, this is going to be a very large XML file, and you might not be able to make sense of the document by reading the source directly. Instead of dealing with this unwieldy XML file, use Apache Felix to browse the contents.
If you want to see the contents of this new proxy repository for the Felix OBR repository, install Felix and add a reference to the Nexus proxy repository:
- Download Apache Felix from https://felix.apache.org/documentation/downloads.html
- Unpack the Felix distribution on your local workstation.
- Start Apache Felix with java -jar ./bin/felix.jar
- Add the Snapshots repository as an OBR repository by copying the URL to the repository.xml and passing it to the obr command: obr add-url http://localhost:8081/nexus/content/repositories/felix-obr/.meta/obr.xml
- List the contents of the OBR repository.
- Deploy the Servlet 2.1 bundle.
- Deploy the Apache Felix Web Management Console bundle.
- Start the installed bundles.
These commands are captured in the following screen listing:
felix-1.8.0/
felix-1.8.0/bin/
felix-1.8.0/bin/felix.jar
...
felix-1.8.0/NOTICE
~/programs $ cd felix-1.8.0
~/programs/felix-1.8.0 $ java -jar ./bin/felix.jar
Welcome to Felix.
=================
-> obr add-url http://localhost:8081/nexus/content/repositories/felix-obr/.meta/obr.xml
-> obr list
Apache Felix Bundle Repository (1.2.1, ...)
Apache Felix Configuration Admin Service (1.0.4, ...)
Apache Felix Declarative Services (1.0.8, ...)
Apache Felix EventAdmin (1.0.0)
Apache Felix File Install (1.2.0, ...)
Apache Felix HTTP Service Jetty (1.0.1, ...)
Apache Felix iPOJO (1.2.0, ...)
Apache Felix iPOJO (0.8.0)
Apache Felix iPOJO Arch Command (1.2.0, ...)
Apache Felix iPOJO Composite (1.2.0)
Apache Felix iPOJO Composite (1.0.0, ...)
Apache Felix iPOJO Event Admin Handler (1.2.0, ...)
Apache Felix iPOJO Extender Pattern Handler (1.2.0)
Apache Felix iPOJO Extender Pattern Handler (1.0.0, ...)
Apache Felix iPOJO JMX Handler (1.2.0, ...)
Apache Felix iPOJO Temporal Service Dependency Handler (1.2.0, ...)
Apache Felix iPOJO White Board Pattern Handler (1.2.0, ...)
Apache Felix Log Service (1.0.0)
Apache Felix Metatype Service (1.0.2, ...)
Apache Felix Prefrences Service (1.0.2)
Apache Felix Remote Shell (1.0.2)
Apache Felix Shell Service (1.0.2, ...)
Apache Felix Shell TUI (1.0.2, ...)
Apache Felix UPnP Base Driver (0.8.0)
Apache Felix UPnP Extra (0.4.0)
Apache Felix UPnP Tester (0.4.0)
Apache Felix Web Management Console (1.2.10, ...)
OSGi OBR Service API (1.0.0)
OSGi R4 Compendium Bundle (4.0.0)
Servlet 2.1 API (1.0.0)
-> obr deploy "Servlet 2.1 API"
Target resource(s):
------------------- Servlet 2.1 API (1.0.0)
Deploying...done.
-> obr deploy "Apache Felix Web Management Console"
Target resource(s):
-------------------
Apache Felix Web Management Console (1.2.10)
Required resource(s):
---------------------
Apache Felix Log Service (1.0.0)
Apache Felix Declarative Services (1.0.8)
Apache Felix HTTP Service Jetty (1.0.1)
OSGi R4 Compendium Bundle (4.0.0)
Deploying...done.
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.8.0)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.2.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.2.0)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0)
[ 4] [Installed ] [ 1] Servlet 2.1 API (1.0.0)
[ 5] [Installed ] [ 1] Apache Felix Log Service (1.0.0)
[ 6] [Installed ] [ 1] Apache Felix Declarative Services (1.0.8)
[ 7] [Installed ] [ 1] HTTP Service (1.0.1)
[ 8] [Installed ] [ 1] Apache Felix Web Management Console (1.2.10)
[ 9] [Installed ] [ 1] OSGi R4 Compendium Bundle (4)
-> start 4 5 6 7 8 9
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.8.0)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.2.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.2.0)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.4.0)
[ 4] [Active ] [ 1] Servlet 2.1 API (1.0.0)
[ 5] [Active ] [ 1] Apache Felix Log Service (1.0.0)
[ 6] [Active ] [ 1] Apache Felix Declarative Services (1.0.8)
[ 7] [Active ] [ 1] HTTP Service (1.0.1)
[ 8] [Active ] [ 1] Apache Felix Web Management Console (1.2.10)
[ 9] [Active ] [ 1] OSGi R4 Compendium Bundle (4)
->
Now, if you load http://localhost:8080/system/console in a web browser and supply the default Felix administrative credentials (username "admin" and password "admin"), you will see the Apache Felix administrative web interface. Now that you've made use of the Nexus Proxy repository for the remote Felix OBR repository, the next time you browse the contents of the Felix OBR proxy repository, you are going to see that Nexus has cached all of the referenced bundles as shown in Figure 1.9, “Cached Bundles from a Remote OBR Repository”.
Figure 1.9. Cached Bundles from a Remote OBR Repository
1.11. Grouping OSGi Bundle Repositories
So far in this chapter, you've deployed an artifact to a Maven repository and consumed it using a Virtual OBR repository as a bridge between Maven and OSGi. In the previous recipe, you proxied a remote OBR repository using a Sonatype Nexus proxy repository. In this recipe, you are going to learn how to consolidate multiple OBR repositories into a single URL using a Sonatype Nexus repository group.
1.11.1. Task
You need to consolidate a number of Sonatype Nexus OBR repositories into a single OBR repository interface. You have a collection of Proxy, Virtual, and Hosted OBR repositories, and you want to connect your OSGi aware systems to a single OBR repository, reducing the number of moving parts involved in your current deployment procedures.
1.11.2. Action
Create a OBR repository group in Sonatype Nexus Repository which combines one or more OBR repositories into a single repository. To do this:
- Load the Nexus interface in a web browser by opening the URL http://localhost:8081/nexus.
- Login as an administrative user using the default credentials of admin/admin123 (if you haven't already changed the default password).
- Click on Repositories in the left navigation menu.
- Click on the Add.. button above the list of Sonatype Nexus repositories and groups.
- Select "Repository Group" from the resulting dropdown.
- In the New Repository Group window, supply the following values as shown in Figure 1.10, “Creating a New OBR Group”:
- Group ID: obr-group
- Group Name: OBR Group
- Provider: OBR Group
- Drag the two OBR repositories created in the previous recipes to the Ordered Group Repositories list.
- Click the Save button to create the new Repository Group.
Figure 1.10. Creating a New OBR Group
1.11.3. Detail
To verify that the new OBR repository group is generating the the appropriate XML combining the Felix OBR reposiotry and the Snapshots OBR repository, click on Repositories in the left navigation menu, and then select "OBR Group" from the list of repositories. If you look at the Browse tab for this repository and drill into the .meta/ directory, you can then right-click on the obr.xml file and select Download from the context menu. The URL for this group's repository XML should be http://localhost:8081/nexus/content/groups/obr-group/.meta/obr.xml. Again,bBe warned, this is going to be an even larger XML file than the repository XML for the Felix OBR proxy. Search for mcookbook and note that the repository also contains references to all of the artifacts from the remote Felix OBR repository. The Sonatype Nexus Repository OBR repository group has effectively merged the contents of these two repositories into a single unified interface which will simplify your system's interaction to a single URL.
If you want to see the contents of this repository group, install Felix and add a reference to the Sonatype Nexus Repository group:
- Download Apache Felix from https://felix.apache.org/documentation/downloads.htmlhttp://www.apache.org/dist/felix/felix-1.8.0.tar.gz.
- Unpack the Felix distribution on your local workstation.
- Change directory to the Felix directory.
- Start Apache Felix with java -jar ./bin/felix.jar
- Add the Snapshots repository as an OBR repository by copying the URL to the repository.xml and passing it to the obr command: obr add-url http://localhost:8081/nexus/content/groups/obr-group/.meta/obr.xml
- List the contents of the OBR repository.
- Verify that the group contains bundles from Felix and the bundle from the osgi-project.
These commands are captured in the following screen listing:
--2009-07-14 22:46:19-- http://www.apache.org/dist/felix/felix-1.8.0.tar.gz
Resolving www.apache.org... 140.211.11.130
Connecting to www.apache.org|140.211.11.130|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 901620 (880K) [application/x-gzip]
Saving to: `felix-1.8.0.tar.gz'
2009-07-14 22:46:20 (924 KB/s) - `felix-1.8.0.tar.gz' saved [901620/901620]
~/programs $ tar xvzf felix-1.8.0.tar.gz
felix-1.8.0/
felix-1.8.0/bin/
felix-1.8.0/bin/felix.jar
...
felix-1.8.0/NOTICE
~/programs $ cd felix-1.8.0
~/programs/felix-1.8.0 $ java -jar ./bin/felix.jar
Welcome to Felix.
=================
-> obr add-url http://localhost:8081/nexus/content/groups/obr-group/.meta/obr.xml
-> obr list
Apache Felix Bundle Repository (1.2.1, ...)
Apache Felix Configuration Admin Service (1.0.4, ...)
Apache Felix Declarative Services (1.0.8, ...)
Apache Felix EventAdmin (1.0.0)
Apache Felix File Install (1.2.0, ...)
Apache Felix HTTP Service Jetty (1.0.1, ...)
Apache Felix iPOJO (1.2.0, ...)
Apache Felix iPOJO (0.8.0)
Apache Felix iPOJO Arch Command (1.2.0, ...)
Apache Felix iPOJO Composite (1.2.0)
Apache Felix iPOJO Composite (1.0.0, ...)
Apache Felix iPOJO Event Admin Handler (1.2.0, ...)
Apache Felix iPOJO Extender Pattern Handler (1.2.0)
Apache Felix iPOJO Extender Pattern Handler (1.0.0, ...)
Apache Felix iPOJO JMX Handler (1.2.0, ...)
Apache Felix iPOJO Temporal Service Dependency Handler (1.2.0, ...)
Apache Felix iPOJO White Board Pattern Handler (1.2.0, ...)
Apache Felix Log Service (1.0.0)
Apache Felix Metatype Service (1.0.2, ...)
Apache Felix Prefrences Service (1.0.2)
Apache Felix Remote Shell (1.0.2)
Apache Felix Shell Service (1.0.2, ...)
Apache Felix Shell TUI (1.0.2, ...)
Apache Felix UPnP Base Driver (0.8.0)
Apache Felix UPnP Extra (0.4.0)
Apache Felix UPnP Tester (0.4.0)
Apache Felix Web Management Console (1.2.10, ...)
org.sonatype.mcookbook (1.0.0.SNAPSHOT, ...)
OSGi OBR Service API (1.0.0)
OSGi R4 Compendium Bundle (4.0.0)
Servlet 2.1 API (1.0.0)
->
As anticipated, the list of bundles in the group repository contains all of the bundles from the Apache Felix repository and the bundle from the Snapshots OBR repository - org.sonatype.mcookbook.