OSGI Tutorial - Part 5
OSGI Tutorial - Part 5
The "One Laptop Per Child" project has a great device ready to ship, but there's no Java on there. Let's think about
working together to put Java on OLPC!
Read the Spotlight Archives
Replies: 14 - Pages: 1
Threads: [ Previous | Next ]
ClReply
In our last part we looked at how to register a service. Now we need to work out how to lookup and use that
service from another bundle.
We will put the problem in the context of our requirements, which as before are inspired by Martin Fowler's paper
on dependency injection. We have built a MovieFinder as a service and registered it with the service registry. Now
we want to build a MovieLister that uses the MovieFinder to search for movies directed by a specific director. Our
assumption is that the MovieLister itself will be a service to be consumed by some other bundle, e.g. a GUI
application. The trouble is, OSGi services are dynamic... they come and they go. That means sometimes we want
to call the MovieFinder service but it just isn't available!
So, what should the MovieLister do if the MovieFinder service is not present? Clearly the call to the MovieFinder
is a critical part of the work done by MovieLister , so there only a few choices available to us:
In this article we're going to look at the first two options, as they are quite simple. The third option may not even
make any sense to you yet, but hopefully it will after we look at some of the implications of the first two.
The first thing we need to do is define the interface for the MovieLister service. Copy the following into
osgitut/movies/MovieLister.java :
package osgitut.movies;
import java.util.List;
public interface MovieLister {
List listByDirector(String name);
}
import java.util.*;
import osgitut.movies.*;
import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;
return result;
}
}
This is probably our longest code sample so far! So what's going on here? Firstly you notice that the logic of
actually searching for movies is separated into a doSearch(String,MovieFinder) method, to help us isolate the
OSGi-specific code. Incidentally, the way we're performing the search is pretty stupid and inefficient, but that's not
really important for the purposes of the tutorial. We only have two movies in our database anyway!
The interesting part is in the listByDirector(String name) method, which uses a ServiceTracker object to obtain
a MovieFinder from the service registry. ServiceTracker is a very useful class which abstracts away a lot of
unpleasant detail in the lowest levels of the OSGi API. However we still have to check whether the service was
actually present. We assume that the ServiceTracker will be passed to us in our constructor.
Note that you may have seen elsewhere code that retrieves a service from the registry without using
ServiceTracker . For example, it is possible to use the getServiceReference and getService calls on
BundleContext . However the code you have to write is quite complex and it has to be careful to clear up after
itself. In my opinion, there is very little benefit in dropping down to the low level API, and lots of problems with it.
It's better to use ServiceTracker almost exclusively.
A good place to create a ServiceTracker is in the bundle activator. Copy this code into
osgitut/movies/impl/MovieListerActivator.java :
package osgitut.movies.impl;
import java.util.*;
import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;
import osgitut.movies.*;
Now this activator is starting to look interesting. Firstly in the start method it creates a ServiceTracker object,
which is used by the MovieLister we just wrote. It then "opens" the ServiceTracker which tells it to start tracking
instances of the MovieFinder service in the registry. Then it creates our MovieListerImpl object and registers it in
the service registry under the interface name "MovieLister" . Finally, just for the sake of being able to see
something interesting when we start the bundle, the activator runs a simple search against the MovieLister and
prints the result.
We need to build and install this bundle. I'm not going to give full instructions this time -- you should be able to
refer back to the previous installments and work it out. Remember you also need to create a manifest file, and it
has to refer to the osgitut.movies.impl.MovieListerActivator class as its Bundle-Activator . Also your Import-
Package line needs to include the three packages that we're importing from other bundles, namely
org.osgi.framework , org.osgi.util.tracker and osgitut.movies .
Once you have installed MovieLister.jar into the Equinox runtime, you can start it. At that point you will see one
of two messages, depending on whether the BasicMovieFinder bundle is still running from last time. If it's not
running you will see:
osgi> start 2
Could not retrieve movie list
This will cause the listByDirector() method to hang for up to 5000 milliseconds waiting for the MovieFinder
service to appear. If a MovieFinder service is installed in that time -- or, of course, if it was already there -- then we
will immediately get it an be able to use it.
Generally though I would advise against suspending threads like this. Particularly in this case, it could be
dangerous because the listByDirector() method is actually called from the start method of our bundle activator,
which was called from a framework thread. Activators are meant to return quickly, because a number of other
things need to happen when a bundle is activated. In fact in the worst case we could cause a deadlock, because we
are effectively entering a synchronized block on an object owned by the framework, which might already by
locked by something else. The general guideline is never perform any long-running or blocking operations in a
bundle activator start method, or in any code called directly from the framework.
In the next installment we will take a look at the mysterious third option for dealing with absent dependencies:
"Don't exist in the first place". Stay tuned!
ClReply
1. At 10:28 AM on Feb 23, 2007, BJ Hargrave Javalobby Newcomers
wrote:
In many ServiceTracker examples, we do not see the call to ServiceTracker.close(). If one uses a
ServiceTrackerCustomizer implementation, it may be important to call ServiceTracker.close so the
customizer can be called at removingService for each tracked service to do any untrack processing. This
could include persistently saving data or something. So I think it is always wise to show the close call in
example code. Without the call to close, the framework will properly unregister the ServiceTracker's
ServiceListener and unget any services being tracked when the bundle is stopped but removingService will
not be called. In your example, that is OK since all the default removingService method implementation
does is unget a tracked service.
ClReply
2. At 12:25 PM on Feb 23, 2007, Neil Bartlett Javalobby Regulars wrote:
The example code I gave does actually close the tracker, but perhaps I should draw more attention to that in
the surrounding text.
ClReply
3. At 2:42 PM on Feb 23, 2007, BJ Hargrave Javalobby Newcomers wrote:
Oh. Somehow I missed that. Well... Nevermind. Move along... nothing to see here.
ClReply
4. At 3:49 PM on Feb 23, 2007, Alex Blewitt DeveloperZone Top 100 wrote:
Alex.
ClReply
5. At 3:17 AM on Mar 28, 2007, Basten li Javalobby Newcomers wrote:
I have created the bundles. I think correctly to have a MovieListerInterface.jar (which has only the
MovieLister interface) and a MovieLister.jar (which has the MovieListerImpl and MovieListerActivator)
Framework is launched.
id State Bundle
0 ACTIVE system.bundle_3.2.2.R32x_v20070118
1 ACTIVE MoviesInterface_1.0.0
2 ACTIVE BasicMovieFinder_1.0.0
3 INSTALLED MovieLister_1.0.0
4 INSTALLED MovieListerImpl_1.0.0
osgi> start 3
org.osgi.framework.BundleException: The activator osgitut.movies.impl.MovieListe
rActivator for bundle MovieLister is invalid
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleAct
ivator(AbstractBundle.java:141)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(Bund
leContextImpl.java:962)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(Bundl
eHost.java:317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(Abstrac
tBundle.java:256)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._st
art(FrameworkCommandProvider.java:239)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.
execute(FrameworkCommandInterpreter.java:145)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(F
rameworkConsole.java:293)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(Fra
meworkConsole.java:278)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(Framewo
rkConsole.java:213)
at java.lang.Thread.run(Thread.java:595)
Caused by: java.lang.ClassNotFoundException: osgitut.movies.impl.MovieListerActi
vator
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(Bundl
eLoader.java:402)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(Bundl
eLoader.java:347)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(De
faultClassLoader.java:83)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at org.eclipse.osgi.framework.internal.core.BundleLoader.loadClass(Bundl
eLoader.java:278)
at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleH
ost.java:227)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleAct
ivator(AbstractBundle.java:134)
... 13 more
Nested Exception:
java.lang.ClassNotFoundException: osgitut.movies.impl.MovieListerActivator
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(Bundl
eLoader.java:402)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(Bundl
eLoader.java:347)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(De
faultClassLoader.java:83)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at org.eclipse.osgi.framework.internal.core.BundleLoader.loadClass(Bundl
eLoader.java:278)
at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleH
ost.java:227)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleAct
ivator(AbstractBundle.java:134)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(Bund
leContextImpl.java:962)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(Bundl
eHost.java:317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(Abstrac
tBundle.java:256)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._st
art(FrameworkCommandProvider.java:239)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.
execute(FrameworkCommandInterpreter.java:145)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(F
rameworkConsole.java:293)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(Fra
meworkConsole.java:278)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(Framewo
rkConsole.java:213)
at java.lang.Thread.run(Thread.java:595)
Nested Exception:
java.lang.ClassNotFoundException: osgitut.movies.impl.MovieListerActivator
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(Bundl
eLoader.java:402)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(Bundl
eLoader.java:347)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(De
faultClassLoader.java:83)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at org.eclipse.osgi.framework.internal.core.BundleLoader.loadClass(Bundl
eLoader.java:278)
at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleH
ost.java:227)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleAct
ivator(AbstractBundle.java:134)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(Bund
leContextImpl.java:962)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(Bundl
eHost.java:317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(Abstrac
tBundle.java:256)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._st
art(FrameworkCommandProvider.java:239)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.
execute(FrameworkCommandInterpreter.java:145)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(F
rameworkConsole.java:293)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(Fra
meworkConsole.java:278)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(Framewo
rkConsole.java:213)
at java.lang.Thread.run(Thread.java:595)
osgi>
Wayne,
It was not really necessary to split the interface and the implementation into two bundles, although there's no
problem with that either.
The reason for the error appears to be that you have osgitut.movies.impl.MovieListerActivator as the
activator for both the interface bundle ("MovieLister") and the implenentation bundle ("MovieListerImpl").
However the interface bundle doesn't contain the class osgitut.movies.impl.MovieListerActivator
Simply remove the Bundle-Activator line from the manifest of the interface bundle – you don't need an
activator for a bundle that only exports API to other bundles.
Regards
Neil.
ClReply
9. At 2:49 AM on Apr 12, 2007, wolverine.my Javalobby Newcomers wrote:
Hi!
Why the finderTrack.getSerive() returns null? Why do we want to check this while we already registered the
service on the other side?
Hi wolverine,
We have to check the return from getService() because the service could be registered or unregistered at any
time.
Regards
Neil
ClReply
11. At 9:04 AM on Apr 26, 2007, Cédric GAVA Javalobby Newcomers wrote:
I feel a little bit confused between packages and bundles , as I thought that two different bundles shall be
split into two different packages , and, on the other side, that one package shall contain only one bundle .
Bundle : BasicMovieFinder
BasicMovieFinderImpl (package osgitut.movies.impl)
BasicMovieFinderActivator (package osgitut.movies.impl)
Bundle : MovieLister ?
MovieListerImpl (package osgitut.movies.impl)
MovieListerActivator (package osgitut.movies.impl)
It's not possible to put MovieListerImpl/Activator and BasicMovieFinderImpl/Activator because there must
be only one activator. So I create another Bundle : MovieLister.
But, according to your source, there are in the same package, regardind to java syntax : so, we can have
many bundle in the same package.
One reason for split packages is that your bundles have been refactored but you need to keep the same code
API. This happens in Eclipse, as some of the 'org.eclipse.core.runtime' packages have been moved from the
runtime bundle into org.eclipse equinox.common, whilst keeping the same package name.
(If you do split packages, you should both Export-Package and Import-Package the split package name, by
the way; that prevents weird classloading errors later)
Re: the activator; you're right, a bundle can only have one activator. However, it's fairly easy to set up code
that allows you do run multiple pieces of code (as well as allowing you to merge the activators manually).
For example:
So, whilst you can only have a single Activator class, you can easily chain it to do other pieces of work.
Alex.
ClReply
13. At 3:49 AM on Sep 20, 2007, dshitd Javalobby Newcomers wrote:
Hello Neil,
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Movie Lister
Bundle-SymbolicName: MovieLister
Bundle-Version: 1.0.0
Bundle-Activator: osgitut.movies.impl.MovieListerActivator
Import-Package: org.osgi.framework,org.osgi.util.tracker,osgitut.movies;version="[1.0.0,2.0.0)"
I packaged only those two Lister related classes and the mf into the MovieLister.jar, the MovieInterface.jar
has also been updated with the MovieLister (the interface) in it.
java.lang.NoClassDefFoundError: osgitut/movies/MovieLister
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2357)
at java.lang.Class.getConstructor0(Class.java:2671)
at java.lang.Class.newInstance0(Class.java:321)
at java.lang.Class.newInstance(Class.java:303)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleAct
ivator(AbstractBundle.java:136)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(Bund
leContextImpl.java:970)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(Bundl
eHost.java:346)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(Abstrac
tBundle.java:260)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(Abstrac
tBundle.java:252)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._st
art(FrameworkCommandProvider.java:260)
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.
execute(FrameworkCommandInterpreter.java:145)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(F
rameworkConsole.java:291)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(Fra
meworkConsole.java:276)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(Framewo
rkConsole.java:218)
at java.lang.Thread.run(Thread.java:595)
But, it works fine in the Eclipse envirment.... I would like to know why. Thanks!
ClReply
14. At 8:35 AM on Oct 11, 2007, Divaa Javalobby Newcomers wrote:
Re: Getting Started with OSGi: Consuming a Service
I had no any problem with the service registration example but with tracking service, i am get the following
error message.
Error during the operation Error Invoking Method: org.osgi.framework.BundleException:
java.lang.NullPointerException
Please Help!!
ClReply
15. At 4:27 PM on May 17, 2008, Frederic Conrotte Blooming Javalobby Member wrote:
In my case, to get the MovieLister bundle starting properly, I had first to repackage the MoviesInterfaces
bundle so that it also contains the osgitut.movies.MovieLister class:
java.lang.NoClassDefFoundError: osgitut/movies/MovieLister
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Unknown Source)
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.newInstance0(Unknown Source)
at java.lang.Class.newInstance(Unknown Source)
I think it's coming from the confusion that the same package (osgitut.movies) is :
- defined in both MoviesInterface and MovieLister bundles.
- exported by MoviesInterface
- imported by MovieLister
If you check OSGi R4 specs, on page 52-278, section 3.8.4 Overall Search Order, you will read:
"3. If the class is in a package that is imported using Import-Package or was imported dynamically in a
previous load, then the request is delegated to the exporting bundle’s class loader; (...) If the request is
delegated to an exporting class loader and the class or resource is not found, then the search terminates and
the request fails"
In our case the Framework resolves the MovieLister.mf "Import-Package: osgitut.movies" with the
MoviesInterface.mf "Export-Package: osgitut.movies"
then the osgitut.movies.MovieLister.class has to be package with exporter MoviesInterface or the class
loading request fails.
thre
N P N N N U H R
Get Firefox! Powered by Jive Software Powered by Caucho Resin!
Eclipse(TM) is a registered trademark of the Eclipse Foundation