This page describes the demo that shows the Discovery functionality of Distributed OSGi.

It assumes that you have set up your DOSGi/ZooKeeper based Discovery system as outlined in the DOSGi Discovery page.

Demo design

This demo consists of a display controller node that writes messages on all the available display instances. Display instances are realized as remote OSGi services, which are registered with the ZooKeeper-based Discovery system. The discovery system informs the controller, which is a consumer of the DisplayServices, of any available remote instances of this service.
@@@ add an image here @@@

As with most of the other demos, the actual implementation consists of 3 bundles.

  • A DisplayService interface bundle.
  • A DisplayService implementation bundle.
  • A DisplayService client bundle, which takes the role of the display controller.

The Display Service interface is as follows:

public interface DisplayService {
    boolean displayText(String text);
    String getID();
}

The Display Controller (service consumer)

Let's start with the controller, which is a consumer to the the DisplayService. It's simply using an OSGi ServiceTracker to consume all DisplayService services. It also uses a Scheduled Executor to periodically send test messages to all registered displays. Here's the Activator:

public class Activator implements BundleActivator {
    private ServiceTracker tracker;
    private Map<DisplayService, String> displays = new ConcurrentHashMap<DisplayService, String>();

    public void start(BundleContext bc) throws Exception {
        tracker = new ServiceTracker(bc, DisplayService.class.getName(), null) {
            public Object addingService(ServiceReference reference) {
                Object svc = super.addingService(reference);
                if (svc instanceof DisplayService) {
                    DisplayService d = (DisplayService) svc;
                    System.out.println("Adding display: " + d.getID() + " (" + d + ")");
                    displays.put(d, d.getID());
                }
                return svc;
            }

            public void removedService(ServiceReference reference, Object service) {
                String value = displays.remove(service);
                System.out.println("Removed display: " + value);
                super.removedService(reference, service);
            }            
        };        
        tracker.open();
        // ... scheduler stuff that sends messages to all registered displays ommitted
    }

    public void stop(BundleContext bc) throws Exception {
        tracker.close();
    }
}

No remote-services.xml file!

As we are using Discovery here, there is no static remote service metadata required. So no remote-services.xml file. Discovery will dynamically inform the DOSGi implementation of the locations of available remote services. The DOSGi implemenation will in turn register proxies for the remote services in the local service registry. As a service consumer you simply depend on the service - e.g. by using a ServiceTracker as above, or through a component framework such as Blueprint or DS.

Running the controller

-> install http://repo1.maven.org/maven2/org/apache/cxf/dosgi/samples/cxf-dosgi-ri-samples-discovery-interface/1.2/cxf-dosgi-ri-samples-discovery-interface-1.2.jar
-> start http://repo1.maven.org/maven2/org/apache/cxf/dosgi/samples/cxf-dosgi-ri-samples-discovery-client/1.2/cxf-dosgi-ri-samples-discovery-client-1.2.jar

-> ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (1.8.0)
[   1] [Active     ] [    1] Apache Felix Shell Service (1.2.0)
[   2] [Active     ] [    1] Apache Felix Shell TUI (1.2.0)
[   3] [Active     ] [    1] Apache Felix Bundle Repository (1.4.0)
[   4] [Resolved   ] [    1] OSGi R4 Compendium Bundle (4.1.0)
[   5] [Active     ] [    1] Distributed OSGi Distribution Software Single-Bundle Distribution
[   6] [Active     ] [    1] Distributed OSGI Discovery Sample Client Bundle
[   7] [Active     ] [    1] Distributed OSGI Discovery Sample Interface Bundle

After a brief moment, you will see messages appearing on the controller side. These are the messages sent to all registered displays. Since there are none, they won't show up anywhere just yet.

Sending text to displays: some text 1
Sending text to displays: some text 2
Sending text to displays: some text 3
...

The Remote Displays (service implementation)

Every Display in the system registers a Display Service implementation in the local Service Registry. This happens in the Activator of the service implementation bundle. It adds the properties to make the service available remotely as well:

public class Activator implements BundleActivator {
    private ServiceRegistration reg;

    public void start(BundleContext bc) throws Exception {        
        Dictionary props = new Hashtable();
        
        String host = getHostName(); // obtain the current host name
        int port = getPort();        // find a free port
        
        props.put("service.exported.interfaces", "*");
        props.put("service.exported.configs", "org.apache.cxf.ws");
        props.put("org.apache.cxf.ws.address", "http://" + host + ":" + port + "/display");

        reg = bc.registerService(DisplayService.class.getName(), 
                new DisplayServiceImpl(host + ":" + port), props);
    }

    public void stop(BundleContext bc) throws Exception {
        reg.unregister();
    }
}

Note that the port where the service is made available is dynamically set to be any free port in the system. So you can safely run any number of remote Display Services on the same machine. Because we're using Discovery here, a well-known port is not needed. Registering the service as in the previous code snippet will make it available remotely and publish it to the installed Discovery system.

Once the provider side bundles are started:

-> install http://repo1.maven.org/maven2/org/apache/cxf/dosgi/samples/cxf-dosgi-ri-samples-discovery-interface/1.2/cxf-dosgi-ri-samples-discovery-interface-1.2.jar
-> start http://repo1.maven.org/maven2/org/apache/cxf/dosgi/samples/cxf-dosgi-ri-samples-discovery-impl/1.2/cxf-dosgi-ri-samples-discovery-impl-1.2.jar

-> ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (1.8.0)
[   1] [Active     ] [    1] Apache Felix Shell Service (1.2.0)
[   2] [Active     ] [    1] Apache Felix Shell TUI (1.2.0)
[   3] [Active     ] [    1] Apache Felix Bundle Repository (1.4.0)
[   4] [Resolved   ] [    1] OSGi R4 Compendium Bundle (4.1.0)
[   5] [Active     ] [    1] Distributed OSGi Distribution Software Single-Bundle Distribution
[   6] [Active     ] [    1] Distributed OSGI Discovery Sample Interface Bundle
[   7] [Active     ] [    1] Distributed OSGI Discovery Sample Implementation Bundle

You will start seeing the messages appear on the remote display:

DisplayService [myhost:1816]: some text 145
DisplayService [myhost:1816]: some text 146
... 

You can add more Display Services and they will also receive the messages.

Under the hood in the ZooKeeper server

The Discovery system uses standard OSGi mechanisms to publish the metadata of the services that are exposed remotely in a centralized server, a ZooKeeper server. On the consumer side, the services are looked up in Discovery through standard OSGi mechanisms as well. These mechanisms are independent of ZooKeeper, so they will work with any Distributed OSGi standards-compliant Dicovery implementation.

ZooKeeper comes with a client program (zkCli) that allows you to look in the ZooKeeper virtual filesystem. All of the OSGi-registered services are stored in a virtual directory under the node /osgi/service_registry. The virtual directory name is based on the fully qualified name of the interface that is implemented by the remote service. So in the case of the DisplayService the remote service metadata is stored under /osgi/service_registry/org/apache/cxf/dosgi/samples/discovery/DisplayService:

$ bin/zkCli localhost:2181

-> ls /osgi/service_registry/org/apache/cxf/dosgi/samples/discovery/DisplayService
Processing ls
[myhost#1816##display]

Every instance of a remote service that implements the org.apache.cxf.dosgi.samples.discovery.DisplayService interface will get a virtual file in this location. The virtual file is only there for the lifetime of the service. If you stop the service, or kill the OSGi container that hosts it, its associated file in this directory will disappear.
You can obtain the information stored in the node, to get an idea of the metadata that's being communicated using the Discovery system:

-> get /osgi/service_registry/org/apache/cxf/dosgi/samples/discovery/DisplayService/myhost#52807##display
<?xml version="1.0" encoding="UTF-8"?>
<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
  <endpoint-description>
    <property name="endpoint.framework.uuid" value="b151dd13-69fe-4d99-8006-fcdafeb48db8" />
    <property name="endpoint.id" value="http://myhost:52807/display" />
    <property name="endpoint.package.version.org.apache.cxf.dosgi.samples.discovery" value="0.0.0" />
    <property name="endpoint.service.id" value-type="Long" value="16" />
    <property name="objectClass">
      <array>
        <value>org.apache.cxf.dosgi.samples.discovery.DisplayService</value>
      </array>
    </property>
    <property name="org.apache.cxf.ws.address" value="http://myhost:52807/display" />
    <property name="service.imported" value="true" />
    <property name="service.imported.configs">
      <array>
        <value>org.apache.cxf.ws</value>
      </array>
    </property>
    <property name="service.intents">
      <array>
        <value>SOAP.1_1</value>
        <value>HTTP</value>
        <value>SOAP</value>
      </array>
    </property>
  </endpoint-description>
</endpoint-descriptions>

cZxid = 0x13
ctime = Fri Jun 25 15:57:09 BST 2010
mZxid = 0x13
mtime = Fri Jun 25 15:57:09 BST 2010
pZxid = 0x13
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x1296f9228b40004
dataLength = 1150
numChildren = 0

Other Resources

Introducing Dynamic Discovery into OSGi Distributed Applications - http://blog.akquinet.de/2009/09/23/introducing-dynamic-discovery-into-osgi-distributed-applications/