You need to create you own OSGi bundle project to integrate with the runtime environment created in the previous recipes.
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:
~/examples/osgi/osgi-project $ mvn pax:create-bundle \
-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:
~/examples/osgi/osgi-project $ mvn install pax:provision
...
[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] [pax:provision]
[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.
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”.
This sample project defines a single interface named
ExampleService which offers a single method
scramble().
Example 1.1. ExampleService Interface
package org.sonatype.mcookbook;
/**
* 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
package org.sonatype.mcookbook.internal;
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
package org.sonatype.mcookbook.internal;
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
