from http://java.dzone.com/articles/simple-osgi-service
Lately, I’ve been experimenting more with OSGi, and I want to share some of the examples I’ve put together. The examples involve Felix, Spring Dynamic Modules, and Jetty, though could easily be used with Equinox.
Once I’m finished with these exercises, I’m hoping to compare and
contrast the different approaches I’ve taken, as well as comparing
embedded Jetty with the Equinox Servlet Bridge. I’m a believer that OSGi is a disruptive technology that stands to transform Java development as we know it today.
There are numerous OSGi tutorials, blogs, and samples posted around the web. My OSGi resources page
lists some of these, and if you know of other good resources not
listed, please let me know. When I experiment with a new technology, I
like to focus exclusively on the technology itself, without code
generators, wizards, or other utilities that do half the work for me. I
feel it maximizes the experience. To that end, all of my exercises
avoid using tools like Maven or Eclipse. I find Maven confusing and
using Eclipse for some of these smaller exercises is overkill. Save for
a few necessary tools like Ant and the Felix framework itself, I’ll
roll everything by hand. Hopefully, this helps clarify the core essence
of OSGi.
The first sample is the canonical HelloWorld example
where I create two simple bundles - a client.jar and service.jar.
Again, in OSGi parlance, a bundle is a .jar file. You can find the
working code in My Google Code repository
(the HelloWorld project), complete with the Ant build script, the
bundle cache, and a .bat or .sh script to start Felix depending on your
OS. The only two prerequisites for these samples is Felix (I used
version 1.0.0) and Ant, which you’ll have to download separately. Let’s
walk through the creation of these two simple bundles.
First, let’s look at HelloConsumer in
client.jar. HelloConsumer uses the HelloService to get the hello and
goodbye messages when the client.jar bundle is started and stopped,
respectively. HelloConsumer implements OSGi’s BundleActivator, meaning
Felix will invoke the start method when the bundle is started, and the
stop method when the bundle is stopped. As seen on line 26,
HelloConsumer obtains a reference to the HelloService using the OSGi
ServiceTracker class.
- package com.extensiblejava.hello.client;
-
- import com.extensiblejava.hello.service.HelloService;
-
- import org.osgi.framework.BundleActivator;
- import org.osgi.framework.BundleContext;
- import org.osgi.framework.ServiceReference;
- import org.osgi.util.tracker.ServiceTracker;
-
- public class HelloConsumer implements BundleActivator {
-
- private ServiceTracker helloWorldTracker;
- private HelloService helloService;
-
- public void setService(HelloService helloService) {
- this.helloService = helloService;
- }
-
- public void removeService() {
- this.helloService = null;
- }
-
- public void start(BundleContext context) throws Exception {
- helloWorldTracker = new ServiceTracker(context, HelloService.class.getName(), null);
- helloWorldTracker.open();
- HelloService hello = (HelloService) helloWorldTracker.getService();
-
- if (hello == null) {
- System.out.println("Hello service unavailable on HelloConsumer start");
- } else {
- System.out.println(hello.sayHello());
- }
- }
-
- public void stop(BundleContext context) {
- HelloService hello = (HelloService) helloWorldTracker.getService();
- if (hello == null) {
- System.out.println("Hello service unavailable on HelloConsumer stop");
- } else {
- System.out.println(hello.sayGoodbye());
- }
-
- helloWorldTracker.close();
- }
-
- }
package com.extensiblejava.hello.client;
import com.extensiblejava.hello.service.HelloService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
public class HelloConsumer implements BundleActivator {
private ServiceTracker helloWorldTracker;
private HelloService helloService;
public void setService(HelloService helloService) {
this.helloService = helloService;
}
public void removeService() {
this.helloService = null;
}
public void start(BundleContext context) throws Exception {
helloWorldTracker = new ServiceTracker(context, HelloService.class.getName(), null);
helloWorldTracker.open();
HelloService hello = (HelloService) helloWorldTracker.getService();
if (hello == null) {
System.out.println("Hello service unavailable on HelloConsumer start");
} else {
System.out.println(hello.sayHello());
}
}
public void stop(BundleContext context) {
HelloService hello = (HelloService) helloWorldTracker.getService();
if (hello == null) {
System.out.println("Hello service unavailable on HelloConsumer stop");
} else {
System.out.println(hello.sayGoodbye());
}
helloWorldTracker.close();
}
}
The HelloService and HelloServiceImpl form
the OSGi service. HelloService is a simple Java interface that defines
the API, while HelloServiceImpl implements the BundleActivator and is
the class invoked when the server bundle is started and stopped. As
seen on line 18, HelloServiceImpl registers itself as an OSGi service.
Without registration, HelloConsumer would not be able to use
HelloService as an OSGi service.
- package com.extensiblejava.hello.service.impl;
-
- import java.util.Properties;
- import com.extensiblejava.hello.service.HelloService;
- import org.osgi.framework.BundleActivator;
- import org.osgi.framework.BundleContext;
- import org.osgi.framework.ServiceListener;
- import org.osgi.framework.ServiceEvent;
- import org.osgi.framework.ServiceRegistration;
-
- public class HelloServiceImpl implements HelloService, BundleActivator {
-
- private ServiceRegistration registration;
-
- public void start(BundleContext context) {
- Properties props = new Properties();
- props.put("Language", "English");
- registration = context.registerService(HelloService.class.getName(), this, props);
- }
-
- public void stop(BundleContext context) {
-
- }
-
- public String sayHello() {
- return "Hello World!! ";
- }
-
- public String sayGoodbye() {
- return "Goodbye World!!";
- }
- }
package com.extensiblejava.hello.service.impl;
import java.util.Properties;
import com.extensiblejava.hello.service.HelloService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceRegistration;
public class HelloServiceImpl implements HelloService, BundleActivator {
private ServiceRegistration registration;
public void start(BundleContext context) {
Properties props = new Properties();
props.put("Language", "English");
registration = context.registerService(HelloService.class.getName(), this, props);
}
public void stop(BundleContext context) {
}
public String sayHello() {
return "Hello World!! ";
}
public String sayGoodbye() {
return "Goodbye World!!";
}
}
There’s a bit more magic here that makes this all work. A
wonderful aspect of OSGi is that dependencies must be managed
explicitly, with each bundle’s manifest declaring the packages the
bundle imports and exports. Bundle manifests specify other key pieces
of information, too. Notably, it tells the OSGi environment of the
BundleActivator to invoke on start and stop. If a bundle imports a
package, then another bundle within that OSGi run-time must export that
same package. The client bundle manifest imports the package containing HelloService while the service bundle manifest
exports that same package. Were these imports and exports not
explicitly declared, we’d receive OSGi run-time errors. In our example
above, HelloServiceImpl registers itself as an OSGi service allowing
clients to obtain a reference to HelloService using the OSGi
ServiceTracker.
The client and service OSGi bundles can be
deployed to any OSGi run-time. I’ve used Felix for this example, but
could have used Equinox just as easily. To run these examples, you can
checkout the code from the Google Code repository. Since I include the
bundle cache in the repository, you should be able to start felix and
experiment with OSGi. You’ll likely have to modify the start script
(OS-based) to ensure Felix is in your classpath. Upon starting Felix,
you’ll be asked to enter a profile name. To use the bundle cache
included with the example, simply use HelloWorld as your profile.
If
you’re feeling a bit more adventurous, you can make some changes to the
HelloServiceImpl message and rebuild the project by invoking the Ant
build script. Once compiled, move back into the Felix shell and update
the service bundle using the Felix update command. For a listing of all
Felix commands, simply type ‘help’.
If you want to deploy the
bundles to your own Felix cache, restart Felix and enter a different
profile name. Any name will do, and Felix will create a new bundle
cache for you. Here are some more suggested steps to build, install,
and continue experimenting with the bundles and OSGi:
- Build service by executing ant within service directory
- Build client by executing ant within client directory
- Run startfelix.bat or startfelix.sh depending on your OS
- install the bundles from the felix shell
- install file:service/bin/service.jar
- install file:client/bin/client.jar
- ps
- start {service-bundle-id}
- start {client-bundle-id}
- Experiment by starting and stopping client and service to get a feel for OSGi.
- Now change the service message printed in HelloServiceImpl.java and compile. Then do the following while client is running.
- stop {service-bundle-id}
- update {service-bundle-id}
- start {service-bundle-id}
- stop {client-bundle-id}
When stopping the client bundle, you should see a different goodbye message than what you saw in step 4.
OSGi
offers a very dynamic and adaptable run-time environment. Even when not
using OSGi services, reuse across bundles can occur so long as the
package dependencies are explicitly managed in the bundle manifests.
The benefit with OSGi services is that the OSGi run-time manages the
service lifecycle, however. Many of the presentations I give on
architecture & design talk about the importance of managing .jar
relationships, and I’ve long felt that the .jar file is a great
candidate as a first class component on the Java platform. In the next
post, I’ll explore the simple benefits of an incredibly important
heuristic that states we should “separate interface from
implementation.” You can get a head start by taking a look at the
HelloWorldSpec project on My Google Code repository.
http://techdistrict.kirkk.com/