Showing posts with label OSGi. Show all posts
Showing posts with label OSGi. Show all posts

Saturday, August 15, 2009

Testing OSGi Bundles with Pax Exam, Groovy and Gradle

You can't be an expert on everything. There are a few topics in life I've given up on, forever to be ignorant:

  • Botany - Don't ask me the name of any plant or tree. I won't know.
  • Hardware Specs - Telling me the model number and speed on your new laptop just makes me confused. I stopped reading the Computer Shopper years ago.
  • Maven - I'm happy to use it IF SOMEONE ELSE CREATES THE POM. Life's too short for that much XML.
So what was I to do with Pax Exam? This framework allows you to write JUnit or TestNG tests and then executes them in the OSGi container of your choosing. It's a cool framework but much of the documentation assumes a willingness to scroll through lines and lines of Maven XML. Ugh. Life's too short to grapple with build systems.

So the nut is cracked. I've got Groovy unit tests running across Equinox, Felix, and Knopflerfish from both Gradle and IntelliJ IDEA. No Maven in sight.

Writing Pax Exam Tests in Groovy

Once it was all configured, writing Pax Exam tests were simple. Start with a plain old JUnit 4 test and do two things:
  1. Annotate it to run with the Pax Exam JUnit runner
  2. Configure it to provision the Groovy-all JAR
Here it is, complete with imports and ready for a little copy-paste action.
import org.junit.runner.RunWith
import org.ops4j.pax.exam.junit.JUnit4TestRunner
import org.junit.Test
import org.ops4j.pax.exam.junit.Configuration
import org.ops4j.pax.exam.Option
import static org.ops4j.pax.exam.CoreOptions.*

@RunWith (JUnit4TestRunner)
class GroovyIntegrationTest {

@Configuration
public Option[] configure() {
[
provision(
mavenBundle().groupId('org.codehaus.groovy').artifactId('groovy-all').version('1.6.4')
)
] as Option[]
}

@Test
public void testFramework() {
println 'Hello from Pax-Groovy!'
}
}

A few things to notice... CoreOptions was statically imported, so the methods provision() and mavenBundle() are resolved. The configure() method returns an Array of Option objects, so the "as Option[]" cast is needed. And by default, the tests are going to run on Felix 1.8 (or thereabouts).

Before Pax Exam becomes useful you're going to want to provision your bundle as the system under test, get access to the BundleContext it was loaded with, and specify more containers to run on.

Provisioning your bundle and running on multiple containers is a matter of configuring the test differently:
@Configuration
public Option[] configure() {
[equinox(),
felix(),
knopflerfish(),
provision(
bundle(new File('./../out/production/Filter4osgi.jar').toURI().toString()),
mavenBundle().groupId('org.codehaus.groovy').artifactId('groovy-all').version('1.6.4')
)] as Option[]
}
Here I'm specifying Equinox, Felix, and Knopflerfish. There's also options for allEquinoxVersions, allFelixVersions, and allKnopflerfishVersions, as well as allFrameworks and allFrameworksVersions to test on the world. That options takes a few minutes to execute! Provisioning my own bundle can be done by loading it as a file URL. This example isn't very robust but it proves the concept.

The BundleContext is available as an injectable bean from Pax Exam. If you want the BundleContext in your unit test, to perhaps test that services were registered correctly, then add this as a field:
@Inject
BundleContext bundleContext
And here is a simple test to make sure that my filter4osgi library was correctly installed:
@Test
public void test_BundleIsLoaded() {

def found = bundleContext.bundles.find {
'filter4osgi' == it.symbolicName
}
Assert.assertNotNull('filter4osgi bundle not loaded!', found)
}
Running Pax Exam Tests from Gradle

The Gradle build was slick. Tell Gradle to load your bundle as a file URL and the rest is just boilerplate dependency management:
usePlugin 'groovy'

repositories {
mavenCentral()
flatDir dirs: [
'./../out/production', // filter4osgi jar built earlier
]
}

dependencies {
groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.6.4'
compile(
'org.ops4j.pax.exam:pax-exam:1.0.0',
'org.ops4j.pax.exam:pax-exam-container-default:1.0.0',
'org.ops4j.pax.exam:pax-exam-junit:1.0.0',
'junit:junit:4.5',
'org.osgi:org.osgi.core:4.0.1',
':filter4osgi:',
)
}
"gradle test" is the only target you'll ever need.

Running Pax Exam Tests from IntelliJ IDEA

Your unit tests will show up once for each container you're targeting. So my two tests run against 3 containers shows up as 6 tests in the test runner window, plus a sweet ASCII art logo:

One nice side effect of Maven is that IDEA can generate files off it, so you don't need to worry about the transative dependencies in the IDE, it's all configured for you. When using Gradle you do need to set up the IDE manually. I added the following JARs as dependencies and it all worked fine (with one exception):
commons-discovery-0.4.jar
commons-logging-1.1.jar
easymock-2.4.jar
junit-4.4.jar
log4j-1.2.12.jar
ops4j-base-lang-1.0.0.jar
ops4j-base-monitors-1.0.0.jar
ops4j-base-net-1.0.0.jar
org.osgi.core-4.0.1.jar
osgi-3.4.0.jar
pax-exam-1.1.0-SNAPSHOT.jar
pax-exam-container-default-1.1.0-SNAPSHOT.jar
pax-exam-container-rbc-1.1.0-SNAPSHOT.jar
pax-exam-container-rbc-client-1.1.0-SNAPSHOT.jar
pax-exam-junit-1.1.0-SNAPSHOT.jar
pax-exam-junit-extender-1.1.0-SNAPSHOT.jar
pax-exam-junit-extender-impl-1.1.0-SNAPSHOT.jar
pax-exam-runtime-1.1.0-SNAPSHOT.jar
pax-exam-spi-1.1.0-SNAPSHOT.jar
pax-exam-testng-1.1.0-SNAPSHOT.jar
pax-exam-tutorial-1.1.0-SNAPSHOT.jar
pax-runner-no-jcl-1.1.0.jar
You can surely discard a lot of these JARs. But I added the list of completeness. It's just the IDE setup. I did have one issue with running the tests in the IDEA. Between runs I needed to manually delete my $TEMP_DIR/paxexam_runner_[user] folder. It sounds like no one else is experiencing this issue and I'm using a snapshot I built myself from source. The mailing list has been pretty responsive but it's a mystery error for now.

That's the end of it, folks. I'm going to the back yard to sit in the kiddie pool with my daughter. It's hot as a mutha in my office.

Wednesday, August 12, 2009

Groovy + OSGi: Gradle Makes it Easy

I'm exploring the tooling around Groovy and OSGi for the September Groovy.MN meeting, where I'm presenting Groovy+OSGi Jumpstart (and then on to 2GX!).

In the end, creating OSGi bundles from Groovy classes was pretty darn easy with Gradle. The User Guide documentation wasn't exactly clear, and the current 0.7 sample project didn't do much of anything... so here's the goods while you wait for the updates to released.

A good background on using Groovy and OSGi is the Beginner's Guide to OSGi on the Desktop. It explains bundles and Activators are, and what the Import-Package and Export-Package metadata specifies. In a nutshell: an OSGi bundle is a JAR file with extra data in the MANIFEST.MF. It's dependencies are specified in the Import-Package attribute and it's exported API is specified in the Export-Package attribute. On bundle startup, a method called Activator.start() is called, and on shutdown Activator.shutdown() is called. The bundle replaces the JAR as a unit of deployment, the dependency management metadata replaces JAR Hell, and the Activator replaces "public static void main".

This post will show you a few things:

  • how to implement an Activator in Groovy
  • how valid bundle metadata looks
  • how to create the bundle from Gradle
  • how to load and execute the bundle in Eclipse Equinox
A Groovy Activator
This couldn't be much simpler. Implement the org.osgi.framework.BundleActivator interface in Groovy. You now have knowledge of 4% of the OSGi API (it's only 27 classes, you know).
package org.gradle

import org.osgi.framework.BundleActivator
import org.osgi.framework.BundleContext

public class GradleActivator implements BundleActivator {

public void start(BundleContext context) {
println "Hello from a Groovy Gradle Activator"
}

public void stop(BundleContext context) {
}
}
Notice, I put this in the org.gradle package (it's important later). Now think for a moment about all the public methods on this object... can you name all 16 methods Groovy adds?.

Valid Bundle Metadata
The bundle metadata in the JAR's MANIFEST.MF file needs to specify, at a minimum, a unique SymbolicName, all the ImportPackages that the class will need to run, and all the ExportPackages that other classes will need to run it.

What does this activator need for ImportPackage? It needs to import org.gradle (itself), and it needs to import org.osgi.framework (the interface). You also need to import groovy.lang and a few other Groovy packages so that Groovy method dispatch works correctly.

What does the activator need for ExportPackage? Certainly org.gradle, so that the container can invoke the Activator. What else? This hidden dependencies are in the silent API of Groovy objects. All Groovy objects have a get/setMetaClass that operates on type groovy.lang.MetaClass. So to use the Activator you need to export groovy.lang and a few other packages, even if you don't invoke those methods!

A Gradle Generated MANIFEST.MF

Luckily, the Gradle OSGi plugin handles all of the ImportPackage and ExportPackage dependencies for you. Here is the full MANIFEST that Gradle generates:
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.0
Created-By: 1.6.0_04 (Sun Microsystems Inc.)
Export-Package: org.gradle;uses:="groovy.lang,org.codehaus.groovy.refl
ection,org.codehaus.groovy.runtime,org.osgi.framework,org.codehaus.gr
oovy.runtime.callsite";version="1.0"
Bundle-Version: 1.0
Tool: Bnd-0.0.255
Bnd-LastModified: 1250128067203
Bundle-Name: Example Gradle Activator
Bundle-ManifestVersion: 2
Bundle-Activator: org.gradle.GradleActivator
Import-Package: groovy.lang;version="1.6",org.codehaus.groovy.reflecti
on;version="1.6",org.codehaus.groovy.runtime;version="1.6",org.codeha
us.groovy.runtime.callsite;version="1.6",org.gradle;version="1.0",org
.osgi.framework;version="1.4"
Bundle-SymbolicName: gradle_tooling.osgi
It's a mouthful! Gotta love the wrapping rules on manifest files. Did you know the wrapping is defined not on the number of characters in a line but the number of bytes? Ugh.

Anway, as you can see, the Export-Package and Import-Package are correct. Groovy was discovered and specified. You can also see in the Tool attribute that bnd is used under the covers.

In Gradle, you use the OSGi plugin to simplify the generation of the manifest. The following build file specifies the Equinox dependency (to resolve the OSGi classes) and the Groovy dependency (to compile the Groovy). The "configure(jar.osgi)" begins the OSGi configuration. By specifying the * for import and export packages, bnd is invoked to determine the dependencies. 22 lines of build script gives you a "gradle jar" target that creates a valid bundle:
group = 'gradle_tooling'
version = '1.0'

usePlugin 'groovy'
usePlugin 'osgi'

repositories {
mavenRepo(urls: 'http://repository.jboss.org/maven2/')
}

dependencies {
groovy group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.6.0'
compile( 'org.eclipse:osgi:3.4.3.R34x_v20081215-1030' )
}

configure(jar.osgi) {
version = '1.0'
name = 'Example Gradle Activator'
instruction 'Bundle-Activator', 'org.gradle.GradleActivator'
instruction 'Import-Package', '*'
instruction 'Export-Package', '*'
}

Proof Positive
It's a hello world bundle, right? So let's run it!

Invoke the OSGi container in console mode:
java -jar %osgi_jar% -console
The Gradle script will stick the OSGi jar in the .gradle cache. Mine is at $home/.gradle/cache/org.eclipse/osgi/jars/osgi-3.4.3.R34x_v20081215-1030.jar

Now just install and start the bundle:
osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900

osgi> install file:/d:/libs/groovy-all-1.6-beta-2.jar
Bundle id is 27

osgi> install file:/d:/libs/osgi-1.0.jar
Bundle id is 28

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900
27 INSTALLED groovy-all_1.6.0.beta-2
28 INSTALLED gradle_tooling.osgi_1.0.0

osgi> start 28
Hello from a Groovy Gradle Activator

osgi>

Wheee! It works; hooray for Gradle, Groovy and OSGi... a trifecta of un-suck.

Tuesday, August 4, 2009

IntelliJ IDEA 9 OSGi Support Explored

IntelliJ IDEA 9 (Maia) purports to support OSGi bundles... so I dug in the weekend to find out just what this means.

This blog posts provides a quick start guide for two things: 1) getting the IDEA 9 EAP to publish your library as an OSGi bundle, and 2) getting a second bundle published and running to test your library.

General Impressions
The IDEA 9 OSGi support is a port/conversion of the Osmorc plugin which was available in version 8. In general, the functionality seems stable but sparsely documented. I could find neither a discussion group nor documentation for Osmorc. A lot of the configuration was a process of trial and error. But it does all seem to work, and after investing a few hours it has started to seem straight-forward in hindsight. You'll have to judge for yourself how it compares to Eclipse (that's next on my list!). Osmorc tries to do a lot of auto-magic generation for you, and when I stepped outside the norm (like including Groovy as a test-only dependency) I was confronted with hard to fix failures. However, this may not be a fair criticism. For any serious project, you will want to write a bnd script, not simply use an IDE, and IDEA lets you use a bnd script to drive the OSGi configuration. So at points where the config wizard started to get difficult, I really should have stepped back and created a bnd script.

Getting a Bundle Published
While Eclipse seems to be focused on creating a Plugin (ie Bundle) from scratch using a wizard, IDEA is more focused on including OSGi support on one of your existing libraries using an OSGi facet. There is no "Create OSGi Project" wizard. You simply need to add an OSGi facet to one of your projects. I find this approach more reasonable. But let's not get too far before describing the basics of publishing a bundle:

1. Installing IntelliJ IDEA 9 and OSGi

  • Download and install the Maia EAP
  • Download and install an OSGi SDK (not just the framework jar). I had trouble with Eclipse Equinox, but KnopplerFish worked just fine
2. Configuring IntelliJ IDEA for OSGi
The next step is to tell IDEA about your OSGi SDK. In Settings (Ctrl+Alt+S), go to OSGi->IDE Settings->Framework Definitions and add your framework:
Now tell IDEA to use the framework you just registered. In Settings (Ctrl+Alt+S), go to OSGi->Project Settings and select your framework in the dropdown:

Done!

3. Publishing Your Library
Now let's publish your Java library as an OSGi bundle. In this example, I'm using my filter4osgi library that provides a nice API for LDAP style filters (shameless plug: I'll pay you for bug reports). This step will create the OSGi .jar file and required manifests.
  • Open an existing IDEA project... the fewer dependencies the easier it will be to publish
  • Add the OSGi facet to the project module. Clicking the little plus sign and picking OSGi should do the trick.
A note about the OSGi facet... currently, you're not able to add more than one OSGi facet to a module. For me, this forced me to create a multi-module project in order to produce several OSGi bundles from the same codebase. This seems like a considerable limitation to me but maybe my projects are structured strangely.

The Manifest Generation tab allows you to specify the exported packages for the bundle. I needed to manually add the package I wanted to export, Osmorc did not seem to discover this for me. Again, using bnd is probably the best option here.


There are plenty of other options, but they aren't as important right now. And note, I didn't add an Activator, because my bundle is just a library.

To verify that you published your library correctly, rebuild the application. The jar file should be written to disk. The manifest file with the OSGi headers is pretty simple:
Manifest-Version: 1.0
Export-Package: org.filter4osgi.builder;version="0.0.1"
Bundle-Version: 0.0.1
Tool: Bnd-0.0.337
Bnd-LastModified: 1249412068156
Bundle-Name: filter4osgi
Bundle-ManifestVersion: 2
Created-By: 1.6.0_12 (Sun Microsystems Inc.)
Import-Package: org.filter4osgi.builder;version="0.0"
Bundle-SymbolicName: filter4osgi
Include-Resource: ..\Filter4osgi
4. Running Your Bundle
Running the bundle is simple. Create a new Run/Debug Configuration using the little plus sign:
Just use the Add button to specify your bundle. You can also specify dependent bundles too.
On the Parameters tab, you'll need to specify the OSGi framework to run within:

Now just click Run, and your console output window should have something like this written to it:
Knopflerfish OSGi framework, version 4.1.7
Copyright 2003-2009 Knopflerfish. All Rights Reserved.

See http://www.knopflerfish.org for more information.
Failed to remove existing fwdir C:\Documents and Settings\hdarcy\.IntelliJIdea90\system\osmorc\runtmp1249413066328
Framework launched
Installed: file:///D:/dev/filter4osgi/out/production/Filter4osgi.jar (id#1)
Started: file:///D:/dev/filter4osgi/out/production/Filter4osgi.jar (id#1)
It worked! (Although I have no idea why there is a failure removing "fwdir", whatever that means). Alternately, you can load the jar file into any other container and interact with it that way. One feature that is missing is the ability to interact with the container from IDEA. You cannot issue the stop or uninstall commands. In fact, the only way I found to stop the container is with the big red Stop button. It would be better if you could issue text commands to the container. This doesn't seem like it would be difficult to add.

5. Testing Your Bundle with an OSGi Test Harness
There are a few test utilities for OSGi, such as Pax Exam and the Knopflerfish Junit Support, but all I wanted to do was write a bundle that imported filter4osgi and made sure the classes were available. So I simply added a new module to my project called "filter4osgiIntegrationTest". To this I added the Knopflerfish framework.jar since I needed to create an Activator. Take note, the prior example of exposing a library as a bundle required zero design or runtime dependencies on any OSGi implementation.

Without adding the OSGi facet, I created a new Java class that implemented BundleActivator. IDEA flagged me with a missing facet error and asked if I'd like to create a facet, which I did. Nice!

The Manifest Generation tab was mostly filled in, but again I needed to hand edit the MANIFEST.MF data so that it imported the package from the original filter4osgi bundle and added the root '.' to the bundle classpath (otherwise there were ClassNotFound exceptions).

Now I just implemented the activator to construct a new filter object:
public class ActivatorForTesting implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
FilterBuilder filter = or(
and(
eq("mailbox", "default"),
eq("lang", "EN_US")
),
and(
eq("mailbox", "dflt"),
eq("lang", "EN_CA")
)
);
assert filter.toString().equals("(|(&(mailbox=default) (lang=EN_US)) (&(mailbox=dflt) (lang=EN_CA)))");
}

@Override
public void stop(BundleContext context) throws Exception { }
}
Hey, that's not a bad API for working with LDAP filters, is it?

And now it's time to create the Run/Debug Configuration, which was probably the hardest step in all this. In the Run Config, I needed to specify that both the filter4osgi and filter4osgiIntegrationTest need to be started up, and that the filter4osgi must be started first. You do this in the Bundles tab, and ordering is done by indicating a Start Level:


Now, when I run the config, I should see that both bundles were started. And if I don't get a ClassNotFound error then I know that the filter4osgi exports lined up correctly with the filter4osgiIntegrationTest imports. Which they do:
Knopflerfish OSGi framework, version 4.1.7
Copyright 2003-2009 Knopflerfish. All Rights Reserved.

See http://www.knopflerfish.org for more information.
Failed to remove existing fwdir C:\Documents and Settings\hdarcy\.IntelliJIdea90\system\osmorc\runtmp1249414635312
Framework launched
Installed: file:///D:/dev/filter4osgi/out/production/Filter4osgi.jar (id#1)
Started: file:///D:/dev/filter4osgi/out/production/Filter4osgi.jar (id#1)
Installed: file:///D:/dev/filter4osgi/out/production/filter4osgiIntegrationTest.jar (id#2)
Started: file:///D:/dev/filter4osgi/out/production/filter4osgiIntegrationTest.jar (id#2)
Perfect!

All in all, the support was quite usable. I think starting with a bnd file would have made things easier for me, as well as if IDEA had let me interact directly with the container.Definitely looking forward to the official release!

Sunday, January 18, 2009

Groovy and OSGi Resources

There was a lot of interest in the Begineer's Guide to OSGi on the Desktop post a few weeks ago. Here are a couple follow up items worth checking out...

The Groovy wiki is now updated with a lengthy article on using Groovy in an OSGi bundle. The Groovy+OSGi wikipage shows a few techniques, including:

  1. Loading Groovy as an OSGi service
  2. Writing a Groovy OSGi Service
  3. and Publishing a Service Written in Groovy
All of the sample code is now in Groovy 1.7, available for browsing online or downloadable from subversion:
svn co https://svn.codehaus.org/groovy/trunk/groovy/groovy-core/src/examples/osgi
Enjoy!

Friday, December 26, 2008

Beginner's Guide to OSGi on the Desktop

... a beginner's guide to OSGi on the desktop in which none of the following are discussed: history, politics, the spec, mobile devices, Sun, project JigSaw, any JSR, and certainly not any past spec leads (the poor souls).

Why should you care about OSGi? The screenshot below is a sample Swing app that evaluates Groovy script against any version of Groovy you have installed, despite that fact that these Groovy versions are not compatible and cannot typically be loaded side-by-side into the same JVM. The Groovy versions available in the UI are updated dynamically as new jars become available on your system with no restart of the frame necessary. The whole thing is 229 lines of code, 147 of which are Swing related.

OSGi gave me true sandboxed modules and dynamic service discovery in 82 lines of code and no XML. Wow.



If this doesn't excite you then your heart must be dead and you should seriously consider that MBA program you've been thinking about.

What's so cool about this?
These three versions of Groovy should not be allowed to coexist within the same JVM... they are incompatible. The Java classpath mechanism is a linear search order of a set of directories where classes are loaded. If two libraries use the same name then you have problems. The full set of all dependencies in your application typically needs to be compatible, but not so in an OSGi world. Dependencies can be privately held so that other modules don't see them.

Furthermore, OSGi makes it easy to detect available services and be notified when new services become available. You can handle these notifications any way you like, including loading the service without shutting your application down. So your web application can gracefully handle the rollout of a new "User Authentication Service" without a restart. And this example shows how to do all of this in 82 lines of code and zero XML.

How does OSGi work?
The whole system runs off of special class loaders and extended Jar manifest files. A module, or OSGi "bundle", is packaged as a standard Jar file, and may include other jar files within. The manifest of the jar declares what packages or classes it exports publicly and what packages or classes it uses internally. The specialized class loaders make sure that your module never sees any of the classes from other modules that aren't publicly declared. Thus, an app my reference Groovy 0.3, 1.0, and 1.6 all at the same time. Goodbye classpath, hello modules.

Service discovery is achieved by simply asking the container for registered bundles that declare to export a specific Java interface. Also, registering a listener with the container will notify your app when services come into (and go out of) existence. Your app no longer has a "public static void main". Instead, it has a start and stop method, and the container will call these when your app is started and stopped. Within start you may register yourself as a service or pull services out of the registry (or "context"). Within stop, you'll probably want to release any services you're using.

So what is this container? The OSGi container is simply a Java class you launch. My container jar is less than 1 megabyte in size and I launch it with the command:

java -jar org.eclipse.osgi-3.4.0.jar -console
It gives you a console in which you can install bundles (jar files, remember) by their URL. Once installed, the bundle can be started, stopped, refreshed, and uninstalled. As you can guess, having your bundle started causes your start method to be invoked; stopped and your stop method gets called. When a service is started, any service listeners are notified of the new service, and likewise when stopped. It can be done programmatically too, and couldn't be much easier.

How does this example work?
Running three different Groovy bundles within one UI required a total of 5 components: the user interface (Groovy Evaluator), the interfaces for the services (Groovy Provider), and the three service implementations (Groovy Provider Impl 0.3, 1.0, and 1.6).



The Groovy Provider exports itself as version 1.0. There is nothing in this jar file except a single POJO and the manifest specifying Export-Package: org.sample.provider;version="1.0.0".

The Groovy Provider Implementations are all the same except for the version numbers. They contain a simple POJO implementing the GroovyProvider interface, the associated Groovy jar file, a manifest, and an Activator. An Activator is an OSGi class, and contains the start and stop methods. Since this bundle is a service provider, it registers itself as a service, like so:
public class Activator implements BundleActivator {

ServiceRegistration registration;

public void start(BundleContext context) throws Exception {
GroovyProvider provider = new LegacyInterpreter();
registration = context.registerService(GroovyProvider.class.getName(), provider, null);
}

public void stop(BundleContext context) throws Exception {
registration.unregister();
}
}
* a groovy bundle actually needs to futz with the classloaders because of all the reflection it requires, but that's not important.

The manifest of the implementations need to import the GroovyProvider interface and also export the fact that they provide an implementation of that interface. Among other things, the manifest contains:
Export-Package: uses:="org.sample.provider";version="1.0.0"
Bundle-Activator: org.sample.Activator
Import-Package: org.sample.provider;version="1.0.0",org.osgi.framework;version="1.3.0"
Lastly, the Groovy Evaluator bundle needs to consume the services and register to listen for any new services that come online. The Activator for the evaluator can do something as simple as query the container for active services:
public class GroovyEvaluatorActivator implements BundleActivator {

public void start(BundleContext context) throws Exception {
String serviceName = GroovyProvider.class.getName();
ServiceReference[] references = context.getAllServiceReferences(serviceName, null);
for (ServiceReference ref : references) {
Object serviceHandle = context.getService(ref);
GroovyProvider service =(GroovyProvider)serviceHandle;
... do something with service
}
}
public void stop(BundleContext context) throws Exception {
}
}
But a more robust way is to subclass ServiceTracker and listen for service events. The code that does this is quite simple and can be found at: http://svn.assembla.com/svn/SampleCode/osgi/groovy-evaluator/src/main/java/org/sample/Activator.java. In this example, the JFrame simple adds and removes entries from it's JComboBox when new services are started and stopped.

Running the Application in the Container
I intentionally left this post light on full code listings. The entire source can be downloaded and built from subversion:
svn co http://svn.assembla.com/svn/SampleCode/osgi
Building the source requires Gradle (but not Groovy). Once downloaded, run:
gradle clean compile archive_jar
To run the OSGi console:
java -jar lib/osgi-3.4.0.jar -console
This opens the OSGi console where you can see what services are available. Within the console, install and start the bundles:
osgi> install file:/D:/dev/SampleCode/osgi/groovy-provider/build/groovy-provider-1.0.jar
Bundle id is 18
osgi> install file:/D:/dev/SampleCode/osgi/groovylib-0.3/build/groovy-provider-impl-0.3.jar
Bundle id is 19
osgi> install file:/D:/dev/SampleCode/osgi/groovylib-1.0/build/groovy-provider-impl-1.0.jar
Bundle id is 20
osgi> install file:/D:/dev/SampleCode/osgi/groovy-evaluator/build/groovy-evaluator-1.0.jar
Bundle id is 21
osgi> start 18 19 20 21
I intentionally did not start the Groovy 1.6 library, and the UI shows just the two Groovy versions installed. Now that the UI is open, you can install and start the last bundle and see how the UI updates:
osgi> install file:/D:/dev/SampleCode/osgi/groovylib-1.6/build/groovy-provider-impl-1.6.jar
Bundle id is 22
osgi> start 22
More information
This project's source code is a good place to start. Specifically, you can check to see exactly how the manifest files are created by looking at the build scripts (the several build.gradle files). There are a few other good resources as well. Sunil Patil wrote a 3 part writeup with full code listings on JavaWorld. Part 1 details the basics os a Hello World service. Part 2 describes the Spring DM product and adding Spring to OSGi. And Part 3 covers several web deployment scenarios and issues. Also, Joseph Ottinger has an OSGi for Beginners on The Server Side.

None of these guides gave me the full picture... I only started to grasp it all once I wrote a sample myself. All told, I spent about 12 to 16 hours with OSGi and I'm really happy I did. Java 7 isn't due until early 2010 and few shops will upgrade before late 2010. So the earliest you'll see JigSaw is 2011... 3 years from now.Why wait? Why not just click a few of the above links and get started now?