Forum Controls
Spotlight Features

The Rich Engineering Heritage Behind Dependency Injection

Andrew McVeigh takes us on a tour of the rich heritage behind dependency injection, what it represents, and tells us why its here to stay.

Java, the OLPC, and community responsibility

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: 18 - Pages: 2   [ 1 2 | Next ]
Threads: [ Previous | Next ]
  Click to reply to this thread Reply

Getting Started with OSGi: Declarative Services and Dependencies

At 4:34 PM on Jun 20, 2007, Neil Bartlett Javalobby Regulars wrote:

Welcome back to the EclipseZone "Getting Started with OSGi" tutorial series. Before getting into today's lesson I'd like to remind you that you can find links to all the previous parts of the tutorial on the contents page on my personal blog.

Last time we took our first look at Declarative Services. This time we will look at the service consumer side of Declarative Services. Remember that previously we registered a service under the java.lang.Runnable interface; now we will create a component that depends on that service.

As discussed, the Declarative Services specification is all about letting you focus on the application logic of your code, rather than the OSGi "glue" code that had to be written in previous lessons. With that in mind, we're simply going to dive into the code, but before we do that we need to create a project. Follow the same steps as in the last lesson, but use the project name "SampleImporter".

Now copy the following code from your browser and paste it into the src folder inside the newly created Eclipse project:

package org.example.ds;
 
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.eclipse.osgi.framework.console.CommandProvider;
 
public class SampleCommandProvider1 implements CommandProvider {
	
	private Runnable runnable;
	
	public synchronized void setRunnable(Runnable r) {
		runnable = r;
	}
	public synchronized void unsetRunnable(Runnable r) {
		runnable = null;
	}
	public synchronized void _run(CommandInterpreter ci) {
		if(runnable != null) {
			runnable.run();
		} else {
			ci.println("Error, no Runnable available");
		}
	}
	public String getHelp() {
		return "\trun - execute a Runnable service";
	}
}


This class implements the CommandProvider interface, which is used to extend the set of commands available at the "osgi>" prompt when you run Equinox. The reason for writing a CommandProvider is that it provides a convenient way to test our code interactively. There is a more detailed discussion of command providers in Chris Aniszczyk's article on IBM developerWorks.

Notice that we don't call any OSGi APIs in this class, in fact we don't even need to import anything from the org.osgi.* packages. The service that we depend on – in this case, an instance of java.lang.Runnable – is provided to us through the setRunnable method, and it is taken away using the unsetRunnable method. We can consider this a form of dependency injection .

The other two methods, getHelp and _run are the implementation methods for the command provider. Yes, "_run" is a funny name with its leading underscore, but that is just an odd feature of the Equinox console API, and nothing to do with OSGi or Declarative Services. Methods using the leading-underscore pattern become commands on the Equinox console, so by providing a method called _run we have added a "run" command. Another thing to notice about this class is that we're being careful to ensure that the runnable field is updated and accessed in a thread-safe way. Thread safety is particularly important in OSGi since it is intrinsically multithreaded, but frankly we should always write our code to be thread-safe.

As before, we have to provide an XML file containing the DS declarations to get this to work. Copy the following into OSGI-INF/commandprovider1.xml in your plug-in project:

<?xml version="1.0"?>
<component name="commandprovider1">
	<implementation class="org.example.ds.SampleCommandProvider1"/>
	<service>
		<provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
	</service>
	<reference name="RUNNABLE"
		interface="java.lang.Runnable"
		bind="setRunnable"
		unbind="unsetRunnable"
		cardinality="0..1"
		policy="dynamic"/>
</component>


There's another important step which I neglected to mention last time. (Thanks to Seamus Venasse for pointing it out .) You need to edit the build.properties file in your plug-in project, and check the box next to the OSGI-INF folder. This is necessary to ensure the folder is included when the bundle is exported using Eclipse's export wizard, or built using PDE Build.
Also we need to add the following line to the bundle manifest:


Service-Component: OSGI-INF/commandprovider1.xml


This declaration has two of the same elements that we saw before, the implementation and service nodes. The implementation node provides the name of the class which implements the component, and the service node tells DS to register the component as a service. In this case we are registering under the CommandProvider interface, which is how we let the Equinox console know about the existence of our command provider.

The next element called reference is something we haven't seen before – it declares to DS that our component has a dependency on a service. The name attribute is simply an arbitrary string which names the dependency (we don't need to worry yet about what this is used for) and the interface attribute specifies the name of the interface we depend on. The bind attribute is the name of a method in the implementation class that will be called by DS when a service becomes available, or in other words, when a Runnable service is registered with the Service Registry, DS will obtain a reference to the new service object and supply it to our component using the specified method. Likewise the unbind attribute is the name of a method that will be called by DS when a service we were using becomes unavailable.

The cardinality attribute reveals the real power of DS. This attribute controls whether the dependency is optional or mandatory, and whether it is singular or multiple. The possible values are:

  • 0..1: optional and singular, "zero or one"
  • 1..1: mandatory and singular, "exactly one"
  • 0..n: optional and multiple, "zero to many"
  • 1..n: mandatory and multiple, "one to many" or "at least one"


In this example we chose optional and singular, which means our command provider can cope with the dependent service being unavailable. Looking back at the code for the _run method, you can see it was necessary to do a null check to handle such a situation.

Let's see what happens if we run this bundle. If you still have your SampleExporter bundle from last time, then you should see the following response when you type the "run" command at the osgi> prompt:


Hello from SampleRunnable


Great! This confirms that we have successfully imported the Runnable service that we wrote in the last lesson. Now try using the stop command to turn off the SampleExporter bundle. When you type the "run" command again you should see:


Error, no Runnable available


Which means that DS noticed the Runnable service going away, and called our unsetRunnable method to let us know.

Looking again at the cardinality attribute, what do you think will happen if we change to "1..1", i.e. switch from an optional to a mandatory dependency? Try making that change and restarting Equinox. If the SampleExporter bundle is active when we type "run" then we will see the same message as before: "Hello from SampleRunnable". However, if SampleExporter is inactive then we will see a very different error message than before. In fact, we will see Equinox's console help message, which is its standard response to an unrecognized command. This indicates that our command provider itself has been unregistered by DS! Since the component has a mandatory dependency which cannot be satisfied, DS is forced to deactivate the component and unregister any services it was providing. That in turn means that the Equinox console forgets about the "run" command.

The ease with which we can make changes like this is one of the best reasons to use Declarative Services. Remember that with ServiceTracker we would have had to rewrite a fairly substantial piece of code.

You might be wondering about the policy attribute, since I haven't mentioned it yet. The value of this can be either "static" or "dynamic", and it states whether the component implementation is able to cope with having services dynamically switched. If it is not, then it is necessary for DS to deactivate the component and create a new instance each time the target service changes. As you might expect, this is quite a heavyweight approach, and it's best to code your component classes to support dynamic switching whenever possible. Unfortunately the default value is static, so you have to remember to set it to dynamic explicitly.

So, we've seen optional singular and mandatory singular, but what about multiple dependencies? There may be more than one Runnable registered in the Service Registry, and if we only bind to one of them then the choice of which one we get is arbitrary. Perhaps instead we would like to implement a "runall" command that runs all of the currently registered Runnables.

What would happen if we just changed the cardinality to "0..n"? In a way, that would almost work: instead of calling the setRunnable method just one time, DS will call setRunnable once for each instance of Runnable in the registry. The problem is that the class we wrote will get confused. Instead of setting a single Runnable field, we need to accumulate the Runnables into a collection. Here's the slightly modified version of the class, which you can again copy and paste directly into the src folder of your project:

package org.example.ds;
 
import java.util.*;
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.eclipse.osgi.framework.console.CommandProvider;
 
public class SampleCommandProvider2 implements CommandProvider {
 
	private List<Runnable> runnables =
		Collections.synchronizedList(new ArrayList<Runnable>());
	
	public void addRunnable(Runnable r) {
		runnables.add(r);
	}
	public void removeRunnable(Runnable r) {
		runnables.remove(r);
	}
	public void _runall(CommandInterpreter ci) {
		synchronized(runnables) {
			for(Runnable r : runnables) {
				r.run();
			}
		}
	}
	public String getHelp() {
		return "\trunall - Run all registered Runnables";
	}
}


Now create OSGI-INF/commandprovider2.xml and copy in the following contents:

<?xml version="1.0"?>
<component name="commandprovider2">
	<implementation class="org.example.ds.SampleCommandProvider2"/>
	<service>
		<provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
	</service>
	<reference name="RUNNABLE"
		interface="java.lang.Runnable"
		bind="addRunnable"
		unbind="removeRunnable"
		cardinality="0..n"
		policy="dynamic"/>
</component>


... and finally add this file to the Service-Component header of the manifest, so that it looks like this:


Service-Component: OSGI-INF/commandprovider1.xml,
OSGI-INF/commandprovider2.xml


This declaration essentially the same as before, except we have renamed the bind and unbind methods, and changed the cardinality to "0..n". As an exercise, try registering a few additional Runnable services and check that the "runall" command executes all of them. Next, what do you think will happen if we change the cardinality to "1..n"? Try verifying it does what you expect.

That's all for this lesson. Remember that the OSGi Alliance Community Event is happening next week in Munich, Germany, and I believe it's still not too late to register . See you there!
  Click to reply to this thread Reply
1. At 7:20 AM on Jun 21, 2007, Peter Kriens Javalobby Newcomers wrote:

Re: Getting Started with OSGi: Declarative Services and Dependencies

Very nice article! You really help people to start using OSGi.

One comment, many people are using bnd (http://www.aQute.biz/Code/Bnd) in maven, ant, or stand alone. Bnd has a special syntax for declarative services. Instead of using a separate xml file, you can encode most cases easily in the manifest with the Service-Component header, bnd will expand this syntax into the xml file and refer to it. For example:

Service-Component: org.example.ds.SampleCommandProvider1;
    provide:=org.eclipse.osgi.framework.console.CommandProvider;
    runnable=java.lang.Runnable;
    optional:=runnable;
    dynamic:=runnable


This allows you to keep all the information in a single file. The default for this header is mandatory static, which is very useful for services like Log Service, Configuration Admin service, etc.
  Click to reply to this thread Reply
2. At 6:50 PM on Jun 22, 2007, Juan Manuel Javalobby Newcomers wrote:

Re: Getting Started with OSGi: Declarative Services and Dependencies

I couldnīt run this example with the instructions from the previous article....

I followed this steps to run it under 3.2.2:

1. File | New | Project | plug-in project.
2. Fill Project Name.
3. For the Tarjet Platform select Eclipse Versin 3.2
4. Click Next
5. Clear "Generate Activator ..."
6. Check "This plug-in will make contributions to the UI"
7. Click Finish

This was the only combination that worked, I do not know why... but I noted that the difference is in the maninfest.mf, specifically in :

Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime

I removed the dependency org.eclips.ui and it workd too. I will glad you if you explain this.

Tank you.
  Click to reply to this thread Reply
3. At 7:57 AM on Jul 4, 2007, Meph Javalobby Newcomers wrote:

Re: Getting Started with OSGi: Declarative Services and Dependencies

Hi, I've been following this series with keen interest and I've put it on the required reading list here.

Not sure if you're going to cover exactly this question in a later article but since I can't find a definite answer anywhere else I have to ask:

Is it necessary for bundles containing declared services to be started or can declarative services bootstrap a service from a bundle in the installed & resolved state?

In a standalone RCP application if new bundles are added through the update mechanism they get resolved by the update configurator but don't get started and so it seems that the services can never be found.

The solutions seem to be either add to osgi.bundles in config.ini and do a complete -clean or write code to somehow recongnise and start bundles containing services? I feel sure there must be a better way?

Thanks
MikeG
  Click to reply to this thread Reply
4. At 8:57 AM on Aug 1, 2007, Arash Javalobby Newcomers wrote:

A Service that references wont work

Hi
I dont know if anyone else ran into this odd problem. But whenever I declare an interface as a service AND provide the service listener (reference tag), the service will be provided correctly but the bind and unbind methods are not executed at all. If I comment the <service> - tag out, it works fine.

<?xml version="1.0"?>
<component name="somecomponent">
<implementation class="my.package.User"/>
<!--
<service>
<provide interface="my.package.IUser"/>
</service>
-->
<reference name="whatever" interface="my.package.ICompany" bind="doSomething" unbind="undoSomething" cardinality="1..n" policy="dynamic"/> </component>

does anybody have a similar problem?
  Click to reply to this thread Reply
5. At 2:18 PM on Aug 17, 2007, rashmy Blooming Javalobby Member wrote:

Re: Getting Started with OSGi: Declarative Services and Dependencies

Did you find an answer to your question? Even I would like to find a better way to automatically start the plug-ins. I use a command line utility to make use of services and it is rather bad that I have to go update my config.ini file ever time I have a new plug-in to be added.
  Click to reply to this thread Reply
6. At 3:49 PM on Aug 17, 2007, Alex Blewitt DeveloperZone Top 100 wrote:

Re: Getting Started with OSGi: Declarative Services and Dependencies

As long as DS is installed and started, and you have Bundle-ActivationPolicy: lazy (3.3) or Eclipse-LazyStart: true (3.2), then the other service-providing bundles don't need to be explicitly started; they can come up themselves.

Alex.
  Click to reply to this thread Reply
7. At 9:01 AM on Aug 23, 2007, Meph Javalobby Newcomers wrote:

Re: Getting Started with OSGi: Declarative Services and Dependencies

As far as I can tell from the docs and by experimentation, lazy-start will only activate the bundle when a class from the bundle is loaded. If you never do this the bundle is never started and your declarative services never appear.

For our purposes we wrote some code that would iterate over all installed bundles (previously installed by the eclipse configurator bundle) looking for DS config or activators referenced in their manifest and starting them where necessary.

Mike
  Click to reply to this thread Reply
8. At 2:41 AM on Sep 4, 2007, Heiko Seeberger Javalobby Newcomers wrote:

Re: Getting Started with OSGi: Declarative Services and Dependencies

Mike, I think you are right: Even with Bundle-ActivationPolicy turned on activation only occours when loading the first class from that bundle.

I have got another question: How can I access a component (instance) from a class in the declaring bundle. Is there something like ServiceComponentRegistry.getComponent(String name) or so?

Heiko
  Click to reply to this thread Reply
9. At 4:52 PM on Sep 30, 2007, Gen Javalobby Newcomers wrote:

Re: A Service that references wont work

I had the same "problem", I've been working on it today and I finally I understand why it doesn't work. Actually it isn't a problem, it's perfectly correct and it's working as intended.

"If a delayed component configuration is satisfied, SCR must register the component configuration as a service in the service registry but the activation of the component configuration is delayed until the registered service is requested ." -- Quotation from the OSGi specifications: OSGi Service Platform Service Compendium, p. 285 (http://www2.osgi.org/Specifications/HomePage)

In other words, something else (a client) must use your service, otherwise your component will never be activated (instantiated).

If you want to know exactly what a delayed component is, read the specifications, but basically, if your component specifies a service, then it's a delayed one. It's possible to change that in the XML description, but I haven't tried it (look for immediate component ).

I know your post is 2 months old, but it might help someone else. :)
  Click to reply to this thread Reply
10. At 4:42 AM on Oct 1, 2007, Meph Javalobby Newcomers wrote:

Re: A Service that references wont work

Good to see the thread is still alive :)

True I believe it is all working correctly. But the specific issue I had was that, delayed activation or not, the declaration xml doesn't get read and the service (or service proxy) doesn't get registered until the bundle is started. No registration, nothing can use the service, service doesn't get activated. Catch-22.

Programmatically starting all bundles with declarative services in their manifest bootstraps the services and they start working.

MikeG
  Click to reply to this thread Reply
11. At 9:17 PM on Oct 24, 2007, Alptug Dilek Javalobby Newcomers wrote:

Re: A Service that references wont work

Hi,

I seem to have a problem. The "run" command we added to the OSGi console is not recognized in my case. After I run the application, I can see my bundles with services command:

{java.lang.Runnable}={component.name=samplerunnable, component.id=1, service.id=22}
Registered by bundle: initial@reference:file:../../Users/adilek/workspace/Tutorial/SampleExporter/ [2]
No bundles using service.
{org.eclipse.osgi.framework.console.CommandProvider}={component.name=commandprovider1, component.id=2, service.id=23}
Registered by bundle: initial@reference:file:../../Users/adilek/workspace/Tutorial/SampleImporter/ [8]
No bundles using service.

However when I use "run", the console does not seem to recognize it? It can be hard to know what the problem is with what I wrote here, but I did not modify any code provided here and followed the steps without any mistake this time hopefully. Any ideas?
  Click to reply to this thread Reply
12. At 4:03 PM on Nov 16, 2007, Carlos Devoto Javalobby Newcomers wrote:

Re: Getting Started with OSGi: Declarative Services and Dependencies

Great article. The only criticism I have pertains to your liberal use of synchronization blocks and your disparagement of the 'static' policy as being more 'heavyweight' than the 'dynamic' policy. In an effort to support the dynamic starting and stopping of referenced services, you have completely hamstrung your main service by introducing these synchronize blocks. Any client wishing to avail itself of this service must now obtain a mutex and wait in line like a Soviet proletarian.

If you are building a service that will never be accessed simultaneously by a large number clients, what you have done is OK, but if you are building for massive concurrency, this is absolutely unacceptable. In such a case, unless you expect the referenced service to come and go like a two-collar stree bum, the synchronized blocks are an absolute no-no, which I assume means you will have to switch the policy to 'static'. Am I missing something here?
  Click to reply to this thread Reply
13. At 4:17 PM on Nov 16, 2007, Carlos Devoto Javalobby Newcomers wrote:

Re: Getting Started with OSGi: Declarative Services and Dependencies

I'm posting this again, because the original contained a few minor typos, because I'm a real stickler about such things, and because I was not able to edit the original successfully:

Great article. The only criticism I have pertains to your liberal use of synchronization blocks and your disparagement of the 'static' policy as being more 'heavyweight' than the 'dynamic' policy. In an effort to support the dynamic starting and stopping of referenced services, you have completely hamstrung your main service by introducing these synchronize blocks. Any client wishing to avail itself of this service must now obtain a mutex and wait in line like a Soviet proletarian.

If you are building a service that will never be accessed simultaneously by a large number clients, what you have done is OK, but if you are building for massive concurrency, this is absolutely unacceptable. In such a case, unless you expect the referenced service to come and go like a street bum, the synchronized blocks are an absolute no-no, which I assume means you will have to switch the policy to 'static'. Am I missing something here?
  Click to reply to this thread Reply
14. At 9:48 AM on Nov 19, 2007, Carlos Devoto Javalobby Newcomers wrote:

Re: Getting Started with OSGi: Declarative Services and Dependencies

NOTE: In thinking about this problem over the weekend, I concluded that the best way to solve it would be to leave the policy as 'dynamic', and use a ReentrantReadWriteLock within the component implementation class. Thus, instead of synchronization blocks, I would grab a write lock when executing the code within the bind and unbind methods, and grab a read lock when executing code in the other public service methods. This allows for the public service methods to be called concurrently from multiple clients while still guarding against race conditions that may arise due to invocations of the bind/unbind methods. Any thoughts? How do others deal with these concurrency problems?

thread.rss_message