Jump to: navigation, search


Revision as of 03:45, 3 May 2010 by Ahmed.aadel.remainsoftware.com (Talk | contribs) (How to configure ZooDiscovery container?)

What is ZooDiscovery?

ZooDiscovery is a discovery mechanism that runs as an OSGi service. It leverage Apache ZooKeeper robustness and implements Eclipse ECF Discovery API.(Hence the name!). ZooDiscovery is flexible and easy to configure.

This work is funded and made open by Remain Software and Industrial-TSI


If you use OSGi remote services (See OSGi Compendium Specs chapter 13) you have to know the other end. In large installations this configuration can be quite cumbersome.

At Remain Software we develop software to manage nodes in a network.

We want our nodes to register themselves to us when they are in the network. This is fine if you manage a small office but not if you manage smart lightbulbs in a sky scraper.

As soon as the lightbulb is screwed into its socket, it can tap some power to activate its OSGi runtime. The runtime will activate the ILightBulb interface with methods dim(int), on() and off() as a remote service. Now, how do we get this service to interested parties...

ECF For the Win

The ECF discovery framework enabled us to create a Zookeeper based Discovery implementation. An addition to the already existing JmDNS (Zeroconf/Bonjour) and jSLP implementations.

An Apache Zookeeper server will replicate configuration data between other Zookeeper servers. The Zookeeper servers know each other and clients know one Zookeeper Server. So the smart bulb (which runs OSGi or did I mention that already?) is preconfigured with the address of its nearest Zookeeper server or gets this information dynamically by some kind of IP broadcast.

When the lightbulb publishes its Remote Service, ECF wakes up and publishes this service through the provided Discovery implementations. The Zookeeper discovery provider will immediately notify its nearest peers and the new lightbulb service is registered in all Zookeeper instances. When the Zookeeper instance that is connected to an interested party receives the data, the Discovery implementation will publish this service in its OSGi container.

The Lightbulb Control Center is waiting for the ILightBulb service and creates a UI in its console. The Building Maintainer can now control the lightbulb.


In the spirit of component based development: If you use ECF Discovery and you want the functionality that is provided by Zookeeper, you can replace your existing ECF implementation by this new one. Upgrade without pain.


Downland both bundles: org.apache.zookeeper & org.eclipse.ecf.provider.zookeeper
from CVS server at [1]
Anonymous CVS info:  :pserver:anonymous@ecf1.osuosl.org:/ecf

If you use Eclipse, just open the CVS Repositories perspective and add a new CVS repository by pressing the button in the view toolbar. Then copy everything between quotes here ":pserver:anonymous@ecf1.osuosl.org:/ecf" and press paste (CTRL+V) in the wizard.

If you do not use Eclipse, you can find it here.

You can find the two modules in the plugins directory.

Inner Concepts

ZooDiscovery implements both ECF discovery interfaces: IDiscoveryAdvertiser and IDiscoveryLocator. That is, ZooDiscovery can publish our services and gets us noticed about discovered services. Perfect! But how? A ZooDiscovery instance running at your machine does its job by exchanging data with other ZooDiscovery instance(s) running elsewhere. So each running ZooDiscovery service must know where that other "elsewhere" exactly is. This is why we should first make our ZooDiscovery happy, giving it an IP address to play with.
To keep it smooth, let's take it step by step following these cases:

How to configure ZooDiscovery container?

To tell ZooDiscovery where to look for new services, we use of One of these three configurational properties: "zoodiscovery.flavor.standalone", "zoodiscovery.flavor.centralized" or "zoodiscovery.flavor.replicated" (difference explained later). For now, let's just use property "zoodiscovery.flavor.standalone" for the following illustration. This property accepts as value one or more IP addresses of target machines we want to connect to.

For example, suppose we have 3 ZooDiscovery instances Z1, Z2 and Z3:
Z1 instance running in a machine with IP address ""
Z2 instance running in a machine with IP address ""
Z3 instance running in a machine with IP address ""

and some arbitrary senario's:
Senario 1 - To discover services advertised by by Z1, we should, then, talk to Z1 instance and the property would be set this way: "zoodiscovery.flavor.standalone="".
Senario 2 - To discover services advertised by by Z1and Z2, the property is now set so: "zoodiscovery.flavor.standalone=" ,".
Senario 3 - To discover services advertised by by Z1,Z2 and Z3, the property is now set so: "zoodiscovery.flavor.standalone=" ,,".

Note the comma used to separate the list of the IP's.

In code this would look like this:

IContainer container = null;
try { 
//"ecf.discovery.zookeeper" is the container name we want to initiate.
container = ContainerFactory.getDefault().createContainer("ecf.discovery.zoodiscovery");
} catch(ContainerCreateException e1){ // TODO 
// then one of the senario's 

//Senario 1. We build an ECF ID to use it to connect to

ID target = container.getConnectNamespace().createInstance(  
new String[] { "zoodiscovery.flavor.standalone=" });

//Or senario 2. We build an ECF ID to use it to connect to and

ID target = container.getConnectNamespace().createInstance(  
new String[] { "zoodiscovery.flavor.standalone=," });

//Or Senario 3. We build an ECF ID to use it to connect to, and

ID target = container.getConnectNamespace().createInstance(  
new String[] { "zoodiscovery.flavor.standalone=,," });

// then connect

container.connect(target, null);
// Connected! Our provider is ready then.

// To advertise services we need adapting our container this way:
IDiscoveryAdvertiser discoveryAdvertiser = (IDiscoveryAdvertiser) container.getAdapter(IDiscoveryAdvertiser.class);
//then we enjoy calling IDiscoveryAdvertiser contract methods
// To localize/discover services we need adapting it this way:
IDiscoveryLocator discoveryLocator = (IDiscoveryLocator) container.getAdapter(IDiscoveryLocator.class);
//then we enjoy calling IDiscoveryLocator  contract methods

How to build a ServiceInfo object and publish it

//Some  location
URI uri = URI.create("http://www.example.com");
//Some service priority
int priority = 0;
//Some service weight
int weight = 3;
//Some random service properties
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setProperty("foobar", new String("foobar"));
serviceProperties.setPropertyBytes("foobar1", new byte[] { 1, 2, 3 });
IServiceTypeID serviceTypeID = null;
try {
serviceTypeID = ServiceIDFactory.getDefault().createServiceTypeID(
DiscoveryContainer.getSingleton().getConnectNamespace(),new String[] {"service1","service2"}, new String[] {"someProtocol"});
} catch (IDCreateException e) {//TODO			
//build a service info instance to be published
ServiceInfo  serviceInfo = new ServiceInfo(uri, "myServiceName", serviceTypeID, priority, weight, serviceProperties);
//advertise the service

Tip: If you already have an OSGi ServiceReference of the service in hand, it might be handy to build a ServiceInfo instance this way:

//Using AdvertisedService class means creating a compile-time dependency on "org.eclipse.ecf.provider.zookeeper.core" which may or may not suit your desing. So bear in mind when using it.
IServiceInfo advertised = new AdvertisedService(myServiceReference);

I want to get notified about discovered services

You get notified about discovered services by registering yourself as an IServiceListener. Let's take it for a ride and make an inner listener class to see how lightweight its contract is:

IServiceListener sl = new IServiceListener() {
public void serviceUndiscovered(IServiceEvent anEvent) {
// service lost, do something..
public void serviceDiscovered(IServiceEvent anEvent) {
// new service is in, do something..
//register to get informed
//To register for service discoveries with a specific type, you might add the type as well using this method:
addServiceListener(IServiceTypeID aType, IServiceListener aListener), instead.
//To register for service types discoveries, you might consider registering as IServiceTypeListener

Note: ZooDiscovery tracks OSGi services being registered under IServiceListener or IServiceTypeListener, and add them as listeners so that (if you choose to) you don't have to add them explicitly the way we did just above. This is handy when your design is a bit more dynamic/component driven.

Advanced configuration

ZooDiscovery Flavors

Standalone mode: zoodiscovery.flavor.standalone

being edited...

Centralized mode: zoodiscovery.flavor.centralized

being edited...

Replicated mode: zoodiscovery.flavor.replicated

being edited...

Fine tuning the underlying ZooKeeper

being edited...