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!
Replies:
16 -
Pages:
2
[
12
| Next
]
Threads:
[
Previous
|
Next
]
Welcome back to the EclipseZone OSGi mini-series. Finally, we're ready to get on to services. In my opinion, the service layer is the most exciting part of OSGi, so these next few installments should be fun.
Last time we looked at the example of a
MovieFinder
interface, which we said would be used by a
MovieLister
to search for movies. In fact you may recognize this example -- it is from Martin Fowler's
famous paper
on "Dependency Injection", also known as "Inversion of Control" or IoC.
Recall the problem that IoC tries to solve. A
MovieLister
doesn't particularly care where the raw movie data comes from, so we use the
MovieFinder
interface to hide such details from it. The idea is we can then substitute alternative implementations of
MovieFinder
, such as one that goes to a database or even one that calls an Amazon Web Service, since
MovieLister
only depends on the interface, not any particular implementation.
So far so good, but at some point we have to actually give a concrete implementation of
MovieFinder
to
MovieLister
. We do this by having an external container "push" a suitable object into it, rather than letting
MovieLister
go out and call a lookup method. Hence the term "Inversion of Control". Many such containers have been developed, for example PicoContainer, HiveMind, Spring, and even EJB 3.0. However there is one limiting factor of all these containers to date: they are mostly static. Once a
MovieFinder
is given to a
MovieLister
, it tends to be associated for the lifetime of the JVM.
OSGi also allows us to implement the IoC pattern, but in a dynamic way. It should be possible to dynamically supply implementations of
MovieFinder
to
MovieLister
and later remove them. Then we can hot-swap from an application that looks up movies in a flat text file to an application that looks them up with Amazon Web Services.
It is the Service Layer that helps us do this. Quite simply, we register a
MovieFinder
as a Service in the Service Registry. Later the
MovieLister
can be supplied with that
MovieFinder
Service. A Service therefore is nothing more than a Java object -- a POJO, if you will -- and it is registered under the name of a Java interface (a POJI?).
This time around, we will just look at registering the service with the registry. Later we will look at how to get the service out of the registry and supplied to a
MovieLister
.
We're going to add the
BasicMovieFinder
bundle that we built last time. We don't need to modify any existing classes, we just need to add a bundle activator. So copy this into
osgitut/movies/impl/BasicMovieFinderActivator.java
:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Basic Movie Finder
Bundle-SymbolicName: BasicMovieFinder
Bundle-Version: 1.0.0
Bundle-Activator: osgitut.movies.impl.BasicMovieFinderActivator
Import-Package: org.osgi.framework,
osgitut.movies;version="[1.0.0,2.0.0)"
There are two things added to this manifest since last time. First is the
Bundle-Activator
line, which tells the framework about the new activator for our bundle -- we didn't need one last time. Also I have added
org.osgi.framework
to the imported packages. As our previous version of the bundle didn't interact with the framework, it didn't need to import the OSGi API packages.
Back in the OSGi console, you should still have
BasicMovieFinder.jar
installed from last time. So you just need to tell OSGi to update the bundle, by typing
update N
, where N is the numeric ID of the bundle (which you have found using
ss
). Now start the bundle with the command
start N
and you should see... very little happen.
Actually we've just registered our first service with the OSGi service registry, but unfortunately there's nobody on the "other end", so the registration doesn't produce any visible effect. If we want to reassure ourselves that our code has actually done something, we're going to have to go digging, and we do that with the following command:
services (objectClass=*MovieFinder)
We should see the following output:
{osgitut.movies.MovieFinder}={category=misc, service.id=22}
Registered by bundle: file:BasicMovieFinder.jar [4]
No bundles using service.
Great, our service is registered! And I'd love to go on and tell you how to lookup that service and use it in another bundle, but that will have to wait until another day. In the meantime, see what you can do with the
services
command. For starters try typing
services
without the expression in parentheses afterwards -- that was actually a filter which reduced the number of services displayed to just the one we were interested in. Without the filter you will see all of the registered services. There are a surprisingly large number of them!
Re: Getting Started with OSGi: Registering a Service
Neil:
Good OSGi series articles! Is it possible to add other series articles' links in some place of each series' one? This allows readers to navigate between those series more conveniently.
Best regards,
Jin
in order to get the compile to work I had to change the ":" in the first line to a ";".
Also I'm using 3.3M5eh, with little experience on other versions. What I'm seeing is that I have to invoke the equinox.jar with a "-clean" to get an osgi with only a bundle 0 showing with a ss. I don't know if this is a bug in 3.3M5eh or a feature since the version of equinox you used in the articles.
Re: Getting Started with OSGi: Registering a Service
Hi Richard,
It's not exactly a typo. You have to use colons and forward slashes on Linux, Mac OS etc, but semicolons and backslashes on Windows. It was my assumption that most experienced Java developers would have made the translation automatically
You're right that invoking with -clean brings you back to a clean slate with none of the previously installed bundles present. However it isn't really necessary to do that in order to follow the tutorial. Unfortunately, the output you see from the ss command will inevitably diverge from what I get and what anybody else will get.
Re: Getting Started with OSGi: Registering a Service
I went through this tutorial, but when I start the service (BasicMovieFinder) I am getting ClassNotFoundException for osgi classes. It look the osgi doesn't find its own classes?
org.osgi.framework.BundleException: The activator osgitut.movies.impl.BasicMovieFinderActivator for bundle BasicMovieFinder is invalid
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleActivator(AbstractBundle.java:141)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:962)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:256)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:239)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:145)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:293)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:278)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:213)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: org/osgi/framework/BundleActivator
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.defineClass(DefaultClassLoader.java:160)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.defineClass(ClasspathManager.java:498)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findClassImpl(ClasspathManager.java:468)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClassImpl(ClasspathManager.java:427)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(ClasspathManager.java:410)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(DefaultClassLoader.java:188)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findLocalClass(BundleLoader.java:334)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:386)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:347)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:83)
at java.lang.ClassLoader.loadClass(Unknown Source)
at org.eclipse.osgi.framework.internal.core.BundleLoader.loadClass(BundleLoader.java:278)
at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleHost.java:227)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleActivator(AbstractBundle.java:134)
... 13 more
Re: Getting Started with OSGi: Registering a Service
Neil,
I think that this tutorial is helping me understand the OSGI much better that just reading the specs.
However, I am getting the same problem as Amine. I have checked the pathing in the BasicMovieFinder.mf. It looks like the following:
BasicMovieFinder.mf
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Basic Movie Finder
Bundle-SymbolicName: BasicMovieFinder
Bundle-Version: 1.0.0
Bundle-Activator: osgitut.movies.impl.BasicMovieFinderActivator
Import-Package: org.osgi.framework,
osgitut.movies;version="[1.0.0,2.0.0)"
What else is there to check? Is there a software bundle that has all the source files in it that could be downloaded. I might have as spelling mistake some where.
Any help appreciated.
Wayne
Re: Getting Started with OSGi: Registering a Service
The formatting of the post may be screwing it up, but you need at least one space for the 'osgitut.movies' line below. You can also put them on one line e.g.
Re: Getting Started with OSGi: Registering a Service
If I have a service which will loop forever... e.g. monitoring a directory until there is one input file available; Do we need to start another thread for this process?
Re: Getting Started with OSGi: Registering a Service
wolverine,
Yes. Whenever the OSGi framework calls into your bundle, e.g. the BundleActivator.start() method, it expects you to return control to it in a reasonable amount of time.
You should therefore avoid writing infinite loops or doing anything that might block indefinitely, e.g. performing blocking I/O or monitor acquisition without a timeout. Otherwise you risk starving another bundle.
Re: Getting Started with OSGi: Registering a Service
NB if this happens, the bundle's state is shown as STARTING in the console. Depending on how many threads that the framework kicks off to start bundles, if you have too many of them it may prevent others from starting.
Of course, there's nothing to stop you starting off another Thread in the start() method; in fact, a number of things do this (e.g. the Jetty web server). You should check, though, that when the activator's stop() method is called, that it terminates any such started thread in a friendly way.
Getting Started with OSGi: Registering a Service
At 10:26 AM on Feb 16, 2007, Neil Bartlett
wrote:
Last time we looked at the example of a
MovieFinderinterface, which we said would be used by aMovieListerto search for movies. In fact you may recognize this example -- it is from Martin Fowler's famous paper on "Dependency Injection", also known as "Inversion of Control" or IoC.Recall the problem that IoC tries to solve. A
MovieListerdoesn't particularly care where the raw movie data comes from, so we use theMovieFinderinterface to hide such details from it. The idea is we can then substitute alternative implementations ofMovieFinder, such as one that goes to a database or even one that calls an Amazon Web Service, sinceMovieListeronly depends on the interface, not any particular implementation.So far so good, but at some point we have to actually give a concrete implementation of
MovieFindertoMovieLister. We do this by having an external container "push" a suitable object into it, rather than lettingMovieListergo out and call a lookup method. Hence the term "Inversion of Control". Many such containers have been developed, for example PicoContainer, HiveMind, Spring, and even EJB 3.0. However there is one limiting factor of all these containers to date: they are mostly static. Once aMovieFinderis given to aMovieLister, it tends to be associated for the lifetime of the JVM.OSGi also allows us to implement the IoC pattern, but in a dynamic way. It should be possible to dynamically supply implementations of
MovieFindertoMovieListerand later remove them. Then we can hot-swap from an application that looks up movies in a flat text file to an application that looks them up with Amazon Web Services.It is the Service Layer that helps us do this. Quite simply, we register a
MovieFinderas a Service in the Service Registry. Later theMovieListercan be supplied with thatMovieFinderService. A Service therefore is nothing more than a Java object -- a POJO, if you will -- and it is registered under the name of a Java interface (a POJI?).This time around, we will just look at registering the service with the registry. Later we will look at how to get the service out of the registry and supplied to a
MovieLister.We're going to add the
BasicMovieFinderbundle that we built last time. We don't need to modify any existing classes, we just need to add a bundle activator. So copy this intoosgitut/movies/impl/BasicMovieFinderActivator.java:package osgitut.movies.impl; import org.osgi.framework.*; import osgitut.movies.*; import java.util.Properties; import java.util.Dictionary; public class BasicMovieFinderActivator implements BundleActivator { private ServiceRegistration registration; public void start(BundleContext context) { MovieFinder finder = new BasicMovieFinderImpl(); Dictionary props = new Properties(); props.put("category", "misc"); registration = context.registerService( MovieFinder.class.getName(), finder, props); } public void stop(BundleContext context) { registration.unregister(); } }Now replace the content of
BasicMovieFinder.mf:There are two things added to this manifest since last time. First is the
Bundle-Activatorline, which tells the framework about the new activator for our bundle -- we didn't need one last time. Also I have addedorg.osgi.frameworkto the imported packages. As our previous version of the bundle didn't interact with the framework, it didn't need to import the OSGi API packages.Now you can rebuild
BasicMovieFinder.jar:Back in the OSGi console, you should still have
BasicMovieFinder.jarinstalled from last time. So you just need to tell OSGi to update the bundle, by typingupdate N, where N is the numeric ID of the bundle (which you have found usingss). Now start the bundle with the commandstart Nand you should see... very little happen.Actually we've just registered our first service with the OSGi service registry, but unfortunately there's nobody on the "other end", so the registration doesn't produce any visible effect. If we want to reassure ourselves that our code has actually done something, we're going to have to go digging, and we do that with the following command:
We should see the following output:
{osgitut.movies.MovieFinder}={category=misc, service.id=22} Registered by bundle: file:BasicMovieFinder.jar [4] No bundles using service.Great, our service is registered! And I'd love to go on and tell you how to lookup that service and use it in another bundle, but that will have to wait until another day. In the meantime, see what you can do with the
servicescommand. For starters try typingserviceswithout the expression in parentheses afterwards -- that was actually a filter which reduced the number of services displayed to just the one we were interested in. Without the filter you will see all of the registered services. There are a surprisingly large number of them!16 replies so far (
Post your own)
Re: Getting Started with OSGi: Registering a Service
Neil:Good OSGi series articles! Is it possible to add other series articles' links in some place of each series' one? This allows readers to navigate between those series more conveniently.
Best regards,
Jin
Re: Getting Started with OSGi: Registering a Service
Jin,Funny you should ask...
http://neilbartlett.name/blog/osgi-articles/
I will be keeping the links on that page updated as each new article comes out.
Glad you're enjoying them!
Neil
Re: Getting Started with OSGi: Registering a Service
Neil, good blog, thanks^_^Re: Getting Started with OSGi: Registering a Service
Great set of articles !Especially for someone like me who is just learning about the insides of eclipse & equinox.
I think there is a small typo.
In
in order to get the compile to work I had to change the ":" in the first line to a ";".
Also I'm using 3.3M5eh, with little experience on other versions. What I'm seeing is that I have to invoke the equinox.jar with a "-clean" to get an osgi with only a bundle 0 showing with a ss. I don't know if this is a bug in 3.3M5eh or a feature since the version of equinox you used in the articles.
Re: Getting Started with OSGi: Registering a Service
Hi Richard,It's not exactly a typo. You have to use colons and forward slashes on Linux, Mac OS etc, but semicolons and backslashes on Windows. It was my assumption that most experienced Java developers would have made the translation automatically
You're right that invoking with -clean brings you back to a clean slate with none of the previously installed bundles present. However it isn't really necessary to do that in order to follow the tutorial. Unfortunately, the output you see from the ss command will inevitably diverge from what I get and what anybody else will get.
Regards
Neil
Re: Getting Started with OSGi: Registering a Service
I went through this tutorial, but when I start the service (BasicMovieFinder) I am getting ClassNotFoundException for osgi classes. It look the osgi doesn't find its own classes?org.osgi.framework.BundleException: The activator osgitut.movies.impl.BasicMovieFinderActivator for bundle BasicMovieFinder is invalid
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleActivator(AbstractBundle.java:141)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:962)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:256)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:239)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:145)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:293)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:278)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:213)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: org/osgi/framework/BundleActivator
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.defineClass(DefaultClassLoader.java:160)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.defineClass(ClasspathManager.java:498)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findClassImpl(ClasspathManager.java:468)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClassImpl(ClasspathManager.java:427)
at org.eclipse.osgi.baseadaptor.loader.ClasspathManager.findLocalClass(ClasspathManager.java:410)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findLocalClass(DefaultClassLoader.java:188)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findLocalClass(BundleLoader.java:334)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:386)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:347)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:83)
at java.lang.ClassLoader.loadClass(Unknown Source)
at org.eclipse.osgi.framework.internal.core.BundleLoader.loadClass(BundleLoader.java:278)
at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleHost.java:227)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleActivator(AbstractBundle.java:134)
... 13 more
Re: Getting Started with OSGi: Registering a Service
Hi Amine,Could you check whether the package
org.osgi.frameworkis listed in theImport-Packagesection of your bundle manifest?Regards,
Neil
Re: Getting Started with OSGi: Registering a Service
Neil,Yes that was the problem. When I updated "BasicMovieFinder.mf" I wrote it in the wrong path. Thanks.
Amine.
Re: Getting Started with OSGi: Registering a Service
Neil,I think that this tutorial is helping me understand the OSGI much better that just reading the specs.
However, I am getting the same problem as Amine. I have checked the pathing in the BasicMovieFinder.mf. It looks like the following:
BasicMovieFinder.mf
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Basic Movie Finder
Bundle-SymbolicName: BasicMovieFinder
Bundle-Version: 1.0.0
Bundle-Activator: osgitut.movies.impl.BasicMovieFinderActivator
Import-Package: org.osgi.framework,
osgitut.movies;version="[1.0.0,2.0.0)"
What else is there to check? Is there a software bundle that has all the source files in it that could be downloaded. I might have as spelling mistake some where.
Any help appreciated.
Wayne
Re: Getting Started with OSGi: Registering a Service
The formatting of the post may be screwing it up, but you need at least one space for the 'osgitut.movies' line below. You can also put them on one line e.g.Import:Package:org.osgi.framework,osgitut.movies
Alex.
Re: Getting Started with OSGi: Registering a Service
That seems to fix the problem. Spaces in the paramater line due make a difference. ThanksWayne
Re: Getting Started with OSGi: Registering a Service
If I have a service which will loop forever... e.g. monitoring a directory until there is one input file available; Do we need to start another thread for this process?Re: Getting Started with OSGi: Registering a Service
wolverine,Yes. Whenever the OSGi framework calls into your bundle, e.g. the BundleActivator.start() method, it expects you to return control to it in a reasonable amount of time.
You should therefore avoid writing infinite loops or doing anything that might block indefinitely, e.g. performing blocking I/O or monitor acquisition without a timeout. Otherwise you risk starving another bundle.
Neil
Re: Getting Started with OSGi: Registering a Service
NB if this happens, the bundle's state is shown as STARTING in the console. Depending on how many threads that the framework kicks off to start bundles, if you have too many of them it may prevent others from starting.Of course, there's nothing to stop you starting off another Thread in the start() method; in fact, a number of things do this (e.g. the Jetty web server). You should check, though, that when the activator's stop() method is called, that it terminates any such started thread in a friendly way.
Alex.