Distributed OSGi applications with Apache CXF DOSGi

This is the first post of the Dynamokos series. This series explores how to build dynamic distributed web applications with OSGi step by step.

Inside this post

In this post, we will show how to:

  1. Use Apache CXF DOSGi to expose an OSGi service as a web service
  2. Use Apache CXF DOSGi to import the remote service in another OSGi framework
  3. Consume the imported service

The code of the post is available from the Dynamokos Google Project.
This post is very similar to the iPOJO / DOSGi tutorial.

Architecture

Architecture of the dynamokos application
Architecture of the dynamokos application

The application uses Apache Felix as OSGi implementation. Two instances of Felix are required, one for the oracle and one for the web part.
Apache Felix iPOJO is used as component model because of its simplicity. It allows for a very clean way to export OSGi services remotely thanks to ‘property propagation’.
Of course, we use Apache CXF DOSGi. This is an implementation of OSGi Distributed Services. This (big) bundle is deployed on both frameworks as it allows both the exportation and the importation (note that it exist a multi-bundle distribution too).
Finally, we use OPS4J Pax Web as HTTP Service implementation. DOSGi embeds it, so we don’t need to deploy it separately.

Implementing the Oracle

The application relies on a very advanced Prediction service. The service interface is quite simple and just allows querying the Oracle. Possible answers are those listed in the interface.

public interface Prediction {
    /**
     * Possible answers.
     */
    public String[] predictions = new String[] {
           // ....
    };

    public String getPrediction();

    public String getPrediction(String question);
}

The Oracle component implements the Prediction service. I will not unveil the Oracle prediction algorithm here (you can just look at the code). The only interesting things are:

@Component(propagation=true)
@Provides
public class Oracle implements Prediction {
   // Oracle secret ...
}

The @Component annotation declares an iPOJO Component Type. It also implements the Prediction service (@Provides will mange the service exposition). The propagation attribute (set to true) enables property propagation. This means that configuration properties are pushed/propagate to the provided service.
Note that this implementation DOES NOT have any

  • OSGi code
  • Code related to distribution

However, this just declares a component type, not an instance. The instance declaration is made in an XML file (iPOJO also offers other ways, but it’s not the topic of this post):

<instance component="de.akquinet.gomobile.dynamokos.oracle.Oracle">
      <property
            name="osgi.remote.interfaces" value="*"/>
      <property 
            name="osgi.remote.configuration.type" 
            value="org.apache.cxf.ws" />
      <property 
            name="osgi.remote.configuration.pojo.address" 
            value="http://localhost:9090/oracle" />
 </instance>

As you can see, I add a couple of properties in the instance configuration. Thanks to the propagation, those properties will be automatically published in the OSGi Service registration (Prediction service).

DOSGi tracks those properties. Once found, it exposed the OSGi service as a remote service according to the given properties. Here, it will be a web service, and every exposed service interface will be remotely accessible. This simple mechanism is very powerful, as it does not impact the service implementation, and allows an administrator to expose services dynamically.

Launching the Oracle

So lets have a look at the Oracle in action.

First, download the dynamokos-runtime from here. Then, unzip it and go in the runtime folder (with a terminal). In this post, I do not explain how to compile it, instruction are also given in the project documentation.

The folder contains preconfigured versions of Felix. To launch the oracle platform, just execute:

java \ 
  -Dfelix.config.properties=file:./conf/oracle-dosgi.properties \ 
  -jar bin/felix.jar cache/oracle-dosgi

This launches Felix, prints a lot of stuff (DOSGi is a little bit verbose) and deploys required bundles:

ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active] [    0] System Bundle (2.0.0)
[   1] [Active] [    1] Distributed OSGi ... (1.1.0.SNAPSHOT)
[   2] [Active] [    1] Apache Felix Bundle Repository (1.4.1)
[   3] [Active] [    1] Apache Felix File Install (1.2.0)
[   4] [Active] [    1] Apache Felix iPOJO (1.4.0)
[   5] [Active] [    1] Apache Felix iPOJO Arch Command (1.4.0)
[   6] [Active] [    1] Apache Felix Shell Service (1.4.0)
[   7] [Active] [    1] Apache Felix Shell TUI (1.4.0)
[   8] [Active] [    1] OSGi R4 Compendium Bundle (4.1.0)
[   9] [Active] [    1] Prediction Service Interface (0.0.1.SNAPSHOT)
[  10] [Active] [    1] Oracle (0.0.1.SNAPSHOT)

Note: be sure that the port 9090 is available as well as the 8085.

To check that everything works, open a browser to http://localhost:9090/oracle?wsdl.
You should get the Prediction service WSDL.

WDSL of the Prediction web service
WDSL of the Prediction web service

Importing the oracle

It’s time to import the prediction service in the other framework. As already said, DOSGi also manages this aspect which is what this first post is all about.
So, to import a remote service with DOSGi, you need to create a bundle containing OSGI-INF/remote-services/remote-services.xml

<service-descriptions 
    xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
  <service-description>
    <provide 
    interface=
     "de.akquinet.gomobile.dynamokos.prediction.Prediction" />
    <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/oracle
    </property>
  </service-description>
</service-descriptions>

This file just tells DOSGi to import our Oracle remote service. Note that the service interface and the location are given. Obviously, the bundle exporting the Prediction service interface has to be deployed on this OSGi gateway too.

When DOSGi discovers such files, it creates a proxy and exposes a corresponding local OSGi service for the given remote service.

In Dynamokos, I created an almost empty bundle containing just this one file. Despite it is also possible to embed this file in just any bundle, I prefer separating the distribution system from my business code. Clear separations are always good.

Consuming the remote service

Now that the Oracle is accessible from the second platform (published as an OSGi service), we just need to use it. In the Dynamokos application, a simple servlet uses the Oracle. This servlet allows a web page to display predictions.

@Component
public class OracleServlet extends HttpServlet {
   
    /**
     * iPOJO injects the HTTP Service in this member. 
     */
    @Requires
    private HttpService http;
    
    /**
     * iPOJO injected the Prediction service in this member.
     */
    @Requires
    private Prediction oracle;

    @Validate
    public void start() throws ServletException, 
         NamespaceException {
        // Expose itself
        http.registerServlet("/oracle", this, null, null);
        // Expose the web page
        http.registerResources("/dynamokos", "web", null);        
    }

    @Override
    protected void doGet(HttpServletRequest req, 
             HttpServletResponse resp)
            throws ServletException, IOException {
        String q = req.getParameter("question");
        if (q == null || q.length() == 0) {
            resp.getOutputStream().print(oracle.getPrediction());
        } else {
            resp.getOutputStream().print(oracle.getPrediction(q));
        }       
    }
}

This servlet is quite simple. It is an iPOJO component requiring both the HTTP Service and the Prediction service. When both are available, the start method is called. This method published the web resources (itself, and the web page). When the web page interacts with the servlet, it just uses the oracle member to use the real oracle. No synchronization, no OSGi burden, just simple…

The web page code is also provided in the project.

Trying the ultimate oracle

Finally, it’s time to start the second gateway and to get some predictions.

With another terminal, go back to the runtime folder. Then executes the following command:

java \ 
    -Dfelix.config.properties=file:./conf/client-dosgi.properties \
     -jar bin/felix.jar cache/client-dosgi

This also starts Felix with the adequate bundles:

ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active] [    0] System Bundle (2.0.0)
[   1] [Active] [    1] Distributed OSGi ... (1.1.0.SNAPSHOT)
[   2] [Active] [    1] Apache Felix Bundle Repository (1.4.1)
[   3] [Active] [    1] Apache Felix File Install (1.2.0)
[   4] [Active] [    1] Apache Felix iPOJO (1.4.0)
[   5] [Active] [    1] Apache Felix iPOJO Arch Command (1.4.0)
[   6] [Active] [    1] Apache Felix Shell Service (1.4.0)
[   7] [Active] [    1] Apache Felix Shell TUI (1.4.0)
[   8] [Active] [    1] OSGi R4 Compendium Bundle (4.1.0)
[   9] [Active] [    1] Oracle Importer (0.0.1.SNAPSHOT)
[  10] [Active] [    1] Client UI (0.0.1.SNAPSHOT)
[  11] [Active] [    1] Prediction Service Interface (0.0.1.SNAPSHOT)

Once launched, open a browser to http://localhost:8080/dynamokos/index.html
And ask the Oracle.

Note : Check that the port 8080 is available.

Dynamokos Web View
Dynamokos Web View

Issues and Conclusion

Despite great, this demo has a small issue. Let’s try to stop the oracle platform:

shutdown

Then, try to re-ask the Oracle, the answer is now: “Oracle temporary unavailable.”

Oracle temporary unavailable
Oracle temporary unavailable

In fact, configured like that, DOSGi is not dynamic. So the Prediction service is still published, and subsequently, for local bundles, still usable!

Worse, now shutdown the client platform, restart the platform (without restarting the oracle platform), reload the page and re-ask something. You will get an “Oracle Temporary Unavailable” message. In fact, DOSGi does not check the availability of the service at all — it makes the assumption that the service is always there.

This is fine for static web services, but coming from a dynamic OSGi perspective, we really would like to go further and have the (local) service only be available if the real (remote) service can be reached. Of course this is possible as we will show in the second post of this series by adding a discovery protocol in order to track the availability of the oracle service. So stay tuned!

If you have questions, feel free to post a comment on this post or to contact us.

5 thoughts on “Distributed OSGi applications with Apache CXF DOSGi

  1. There are actually lots of details like that to take into consideration. That could be a great point to bring up. I offer the thoughts above as common inspiration but clearly there are questions just like the one you bring up where crucial thing shall be working in trustworthy good faith. I don?t know if finest practices have emerged around issues like that, but I’m sure that your job is clearly identified as a good game. Both girls and boys feel the impact of just a second’s pleasure, for the rest of their lives.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s