This is a walkthrough of the Distributed OSGi Greeter Demo. It should help users of the Distributed OSGi get started with it.

The greeter demo can be found in the samples/greeter directory of the SVN code base and implements a simple OSGi Greeter Service and a consumer to that service with a trivial UI.
In this walkthrough all the required bundles are installed straight from the maven release repository, so no need to check out SVN and build anything to get started with the Greeter Demo.

The Greeter demo design

The demo is composed of 3 bundles:

  • The Greeter Interface bundle
  • The Greeter Service Implementation bundle
  • The Greeter Service Consumer bundle

The Greeter Interface bundle exports the GreeterService interface which both other bundles depend on. This is the interface:

public interface GreeterService {
    Map<GreetingPhrase, String> greetMe(String name);
    GreetingPhrase [] greetMe(GreeterData name) throws GreeterException;
}

The GreeterData interface, GreetingPhase and GreeterException classes are custom types defined in the Greeter Interface bundle.

In this walkthrough, the following setup will be used:

The client side bundles will be running in Equinox, invoking on a Distributed OSGi Service running in Felix. Note that the choice of containers is completely trivial. Any OSGi container that implements the Service Registry Hooks (a new feature in OSGi 4.2) can be used on either side.

The Server Side

The Greeter Service implementation bundle provides a trivial implementation of the GreeterService interface. Additionally, it has an Activator:

public class Activator implements BundleActivator {
    private ServiceRegistration registration;

    public void start(BundleContext bc) throws Exception {
        Dictionary props = new Hashtable();

        props.put("service.exported.interfaces", "*");
        props.put("service.exported.configs", "org.apache.cxf.ws");
        props.put("org.apache.cxf.ws.address", "http://localhost:9090/greeter");
        
        registration = bc.registerService(GreeterService.class.getName(), new GreeterServiceImpl(), props);
    }

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

Besides creating the service instance, the activator sets the additional properties on the service that are required to make it available remotely:

  • The service.exported.interfaces property is set to *, which means that all the interfaces passes to registerService should be made accessible remotely. In this case it's just the GreeterService interface.
  • The service.exported.configs is set to org.apache.cxf.ws, which is a CXF specific configuration type that can be used to expose the OSGi Service as a Web Service over SOAP/HTTP. The address of the service is specified via the org.apache.cxf.ws.address property.

Let's run the server in Felix 3.0.1. As a prerequisite it requires some of the OSGi Compendium Specification interfaces. These don't come with the Felix download, but you can install a bundle that contains these interfaces straight from Maven.
In this walkthrough I'm using the single-bundle distribution of CXF/DOSGi which can be installed straight from the Maven release repository.

To set up my Felix environment, I'm running the following commands:

C:\felix-framework-3.0.1>

Welcome to Apache Felix Gogo
g! install http://repo1.maven.org/maven2/org/osgi/org.osgi.compendium/4.2.0/org.osgi.compendium-4.2.0.jar
g! start http://www.apache.org/dist/cxf/dosgi/1.2/cxf-dosgi-ri-singlebundle-distribution-1.2.jar
... some log messages may appear...
g! lb
    0|Active     |    0|org.apache.felix.framework (3.0.1)
    1|Active     |    1|org.apache.felix.bundlerepository (1.6.2)
    2|Active     |    1|org.apache.felix.gogo.command (0.6.0)
    3|Active     |    1|org.apache.felix.gogo.runtime (0.6.0)
    4|Active     |    1|org.apache.felix.gogo.shell (0.6.0)
    5|Resolved   |    1|osgi.cmpn (4.2.0.200908310645)
    6|Active     |    1|cxf-dosgi-ri-singlebundle-distribution (1.2.0)

Some log messages may come up now.

Now let's start up the server-side greeter bundles. Like with the DOSGi bundle itself, I'm installing these straight from the Maven release repository.

g! start http://repo1.maven.org/maven2/org/apache/cxf/dosgi/samples/cxf-dosgi-ri-samples-greeter-interface/1.2/cxf-dosgi-ri-samples-greeter-interface-1.2.jar
g! start http://repo1.maven.org/maven2/org/apache/cxf/dosgi/samples/cxf-dosgi-ri-samples-greeter-impl/1.2/cxf-dosgi-ri-samples-greeter-impl-1.2.jar
... some log messages will appear ...
g! lb
START LEVEL 1
   ID|State      |Level|Name
    0|Active     |    0|System Bundle (3.0.1)
    1|Active     |    1|Apache Felix Bundle Repository (1.6.2)
    2|Active     |    1|Apache Felix Gogo Command (0.6.0)
    3|Active     |    1|Apache Felix Gogo Runtime (0.6.0)
    4|Active     |    1|Apache Felix Gogo Shell (0.6.0)
    5|Resolved   |    1|osgi.cmpn (4.2.0.200908310645)
    6|Active     |    1|Distributed OSGi Distribution Software Single-Bundle Distribution
    7|Active     |    1|CXF Distributed OSGi Greeter Demo Interface Bundle
    8|Active     |    1|CXF Distributed OSGi Greeter Demo Service Implementation Bundle

At the end of the log messages you will see one appear that says:
INFO: TopologyManager: export sucessful Endpoints: Endpoint Description for ServiceReference [org.apache.cxf.dosgi.samples.greeter.GreeterService]
This means that the service is successfully exposed remotely, and you can verify this by requesting the WSDL:

This WSDL was dynamically generated from the exposed GreeterService Java interface. Under the hood, the Aegis data binding is used for this.

The Service Consumer side.

The Service Consumer Java code contains no specific Distribution-related elements. It's simply an Activator that creates a ServiceTracker listening for services that implement the GreeterService interface. When the service becomes available it opens a little GUI window that asks you for an argument that can be sent to the Greeter service. See here for the actual code

Note that in many cases using an OSGi Component Framework such as Spring-DM, iPojo or OSGi DS is highly recommeded when writing OSGi Service Consumers. OSGi Services are highly dynamic in nature. They can come and go. Using a component framework will generally save you from writing ServiceTracker code as the framework will generally provide the service consumer with a reference to the service via injection.

So the remote service is simply looked up the normal way, via the OSGi Service Registry.
How does it get there? The fact that a lookup on a service is done internally triggers a Service Registry Hook. This will go out to any registered Distributed OSGi Discovery implementations and query them for any matching services.
However, in our setup we haven't yet registered a Discovery implementation. There is an alternative, more static way to provide discovery type information, in case this info is not available via discovery. It can be specified in a OSGI-INF/remote-service/*.xml file. This the content of the Greeter Service Consumer remote-services.xml file:

<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
  <endpoint-description>
    <property name="objectClass">
      <array>
        <value>org.apache.cxf.dosgi.samples.greeter.GreeterService</value>
      </array>
    </property>
    <property name="endpoint.id">http://localhost:9090/greeter</property>
    <property name="service.imported.configs">org.apache.cxf.ws</property>
  </endpoint-description>
</endpoint-descriptions>

Let's run the consumer in Equinox, so that we have the bundles running in Equinox talking to a remoted service running in Felix!
As with Felix, we will have to load the bundle with the OSGi compendium interfaces. There's one that ships with Equinox.

/eclipse> java -jar plugins/org.eclipse.osgi_3.6.0.v20100517.jar -console
osgi> install install file:plugins/org.eclipse.osgi.services_3.2.100.v20100503.jar
Bundle id is 1

osgi> install http://www.apache.org/dist/cxf/dosgi/1.2/cxf-dosgi-ri-singlebundle-distribution-1.2.jar
Bundle id is 2

osgi> start 2
... some log messages may appear...

Some logging messages may appear. You can also automatically load the DOSGi bundles by appending the target/equinox.config.ini.append to you equinox config.ini file.

osgi> install http://repo1.maven.org/maven2/org/apache/cxf/dosgi/samples/cxf-dosgi-ri-samples-greeter-interface/1.2/cxf-dosgi-ri-samples-greeter-interface-1.2.jar
Bundle id is 3
osgi> install http://repo1.maven.org/maven2/org/apache/cxf/dosgi/samples/cxf-dosgi-ri-samples-greeter-client/1.2/cxf-dosgi-ri-samples-greeter-client-1.2.jar
Bundle id is 4
osgi> ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.6.0.v20100517
1       RESOLVED    org.eclipse.osgi.services_3.2.100.v20100503
2       ACTIVE      cxf-dosgi-ri-singlebundle-distribution
3       INSTALLED   cxf-dosgi-ri-samples-greeter-interface
4       INSTALLED   cxf-dosgi-ri-samples-greeter-client

osgi> start 4

After a few moments the following window appears:

The window appears once the ServiceTracker in the consumer has received a callback that the GreeterService has been found. The value entered will used to invoke the (remote) OSGi service, with a call like this:

Map<GreetingPhrase, String> result = greeterService.greetMe("foobar");

I can see that the invocation has been received by the service implementation as in the Felix window the following appears.

-> Invoking: greetMe(foobar)

In the Equinox window I can see the response:

osgi> greetMe("foobar") returns:
  Hola foobar
  Bonjour foobar
  Hoi foobar
  Hello foobar