Παρασκευή 26 Οκτωβρίου 2012

OSGi Remote Services

Remote Services is a feature introduced in the 4.2 version of the OSGi specification. It provides a way for accessing OSGi services from remote OSGi containers. CXF DOSGi is an open source implementation of Remote Services.

An example for running an OSGi service in one container and accessing it from another is included in the DOSGi samples. I would like to present here another example, based on Apache Karaf features. The source code of this example is available in GitHub.

The source code consists of the following projects:
  • converter (interface of a conversion service)
  • converter-impl (implementation of conversion service)
  • converter-client (client of conversion service)
  • converter-server-feature (maven host project for converter server feature)
  • converter-client-feature (maven host project for converter client feature)


Temperature Convertion Service

The example implements a temperature conversion feature. The interface of the service is as follows:
public interface Converter {
    double toCelcius(double fahrenheit);
    double toFahrenheit(double celcius);
}

And the implemetation:
public class ConverterImpl implements Converter {
    
    @Override
    public double toCelcius(double fahrenheit) {
        double celcius = ((fahrenheit - 32) * 5) / 9;
        System.out.println("Fahrenheit " + fahrenheit + 
                           " => Celcius " + celcius);
        return celcius;
    }
    
    @Override
    public double toFahrenheit(double celcius) {
        double fahrenheit = 32 + ((celcius*9)/5);
        System.out.println("Celcius " + celcius + 
                           " => Fahrenheit " + fahrenheit);
        return fahrenheit;
    }
}

The implementation bundle requires an Activator that will register the service.
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/converter");
        
        registration = bc.registerService(Converter.class.getName(), 
                                          new ConverterImpl(), props);
    }

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

This is how a simple OSGi service will be registered. The only extra thing is the CXF DOSGi parameters. If the CXF DOSGi bundle (or bundles - both a single and a multi bundle version are available for download) is not present, then these properties are ignored and the service can still be discovered by bundles in the same container. If however the CXF Distributed OSGi component is installed, then the above properties will have the effect of exporting a SOAP Web Service at the http://localhost:9090/converter endpoint. The generated WSDL file is available at http://localhost:9090/converter?wsdl.

Note: It is very easy to export the OSGi service as a RESTful JAX-RS service as well. The only thing needed is to replace 'ws' with 'rs' in the above property values!

If you have downloaded the source code from GitHub, it will be very easy now to test the service implementation. Build the code with maven, start Karaf and type the following in Karaf's command prompt:
features:addURl mvn:gr.atc.aniketos.demos.converterservice/converter-server-feature/1.0.0/xml
features:install converter-server-feature

The above commands will install the interface and the implementation bundles, as well as the single-bundle distribution of CXF DOSGi (The features.xml points to a file system location of the CXF DOSGi bundle. You need to download the file and modify features.xml to point to the downloaded jar file in your file system. You need to do this before building the projects and running the above commands in Karaf).

After running the commands allow for a minute or so for the bundles to install and then point your browser to http://localhost:9090/converter?wsdl. The WSDL file of the conversion service should appear. You can treat this service as a regular SOAP Web Service and consume it through any SOAP Service client. What is more interesting however, is to access it as a remote OSGi service from a bundle running in a different container.


Service Client

The Service Client project  utilizes an Activator that tries to discover a Consumer Service implementation using ServiceTracker.
public void start(final BundleContext bundleContext) throws Exception {
    tracker = new ServiceTracker(bundleContext, Converter.class.getName(), null) {
        @Override
        public Object addingService(ServiceReference reference) {
            Object result = super.addingService(reference);

            // Do something with the service
            useService(bundleContext, reference);

            return result;
        }
    };
    tracker.open();
}

Insice the useServive method a simpe JFrame dialog is started, which provides a basic interface for entering values for conversion.



There isn't a single line of Java code in the client bundle that relates with DOSGi. You can treat the client bundle as a regular OSGi bundle. You will need of course to install it in a container, where a conversion service implementation is also installed. As soon as a service is discovered, the JFrame dialog will appear and you can start doing conversions. Type a temperature in one of the two text fields and then click "Invoke". The result will appear in the command prompt.

Note: This is a very basic example. Things like the service going away aren't treated.

In order to enable the discovery of a remote service, what is needed is a remote-services.xml file. This file must be present in OSGI-INF/remote-service/remote-services.xml location inside the client bundle. The contents of the file are as follows:
<?xml version="1.0" encoding="UTF-8"?>
<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
  <service-description>
    <provide interface="gr.atc.aniketos.demos.converter.Converter"></provide>
    <property name="service.exported.interfaces">*</property>
    <property name="service.exported.configs">org.apache.cxf.ws</property>
    <property name="org.apache.cxf.ws.address">http://localhost:9090/converter</property>
  </service-description>
</service-descriptions>

What we define here is the interface of the service we want to discover and the endpoint to look it up. (We also define if it is about a SOAP or a RESTful service.)

We are now ready to test the client bundle in a different container. Build the projects (change the location of the CXF DOSGi single bundle distribution jar file in converter-client-feature\src\main\resources\features.xml), start a second instance of Karaf and type the following:
features:addURl mvn:gr.atc.aniketos.demos.converterservice/converter-client-feature/1.0.0/xml
features:install converter-client-feature

Note: In order to run two instances of Karaf in the same machine, you need to make some changes in the
etc/org.apache.karaf.management.cfg file. In this file two port numbers for RMI connections are defined. Make sure that the two Karaf instances use different port numbers.

After running the above commands, wait for some seconds for the bundles to install and the remote service to be discovered. Then the conversion dialog will appear. Type a temperature and click "Invoke". The result of the conversion should appear in the command prompt of both instances of Karaf.

You can of course use a different container than Karaf for either the service or the client. In that case however the convenient features mechanism can be used and you will need to find another way for installing the bundles. If you install the service in a different machine than the client, then you need to replace localhost in the org.apache.cxf.ws.address property of the remote-services.xml with the correct address.

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου