Question

I have these bundles:

  • Comm Service provides a communication service.
  • Communication is a communication bundle that uses the Comm Service to send/receive messages.
  • Poll sends poll messages in multicast mode.

The Communication bundle is saturated and my approach is to implement other bundles for specific actions - the Poll bundle will be responsible for sending polls, the Postcard bundle for sending postcards, etc. (just some examples).

Is the approach to pass the Comm Service service as an input parameter for the Poll bundle when the Communication bundle needs to delegate its work of sending polls/postcards/message?

Is the snippet below correct?

Communication bundle code:

PollBundle p = new PollBundleImpl();
p.sendPoll(String pollQuestion, CommService cs);

Or is the better approach to let the Poll/Postcard bundles to retrieve the Comm Service service on their own?

Was it helpful?

Solution

You must not pass the service object between bundles; if you do this then the OSGi Framework loses control over which bundles have visibility of the service and therefore it cannot inform the full set of bundles when that service needs to go away.

Therefore the Poll bundle must find the CommService for itself. There are a number of ways of doing this. I strongly recommend using Declarative Services from the OSGi Specification. Used in conjunction with bnd annotations you will be able to write the code of your Poll bundle as follows:

@Component
public class Polling {

    private CommService commService;

    @Reference
    public void setCommService(CommService cs) {
        this.commService = cs;
    }

    public void someMethodThatUsesCommService() {
         sendPoll(cs);
         // ...
    }
}

I recommend this approach over the Blueprint solution described by Jacek Laskowski in his answer because it does not require writing verbose and non-typesafe XML files, and it has better lifecycle characteristics that match the lifecycle of OSGi Services. However both the Declarative Services and Blueprint approaches are certainly safer than mucking about with the low-level OSGi APIs (e.g. ServiceTracker, ServiceListener).

OTHER TIPS

I'd highly recommend to decouple the bundles leveraging dependency injection of the OSGi Blueprint (see Blueprint Container Specification in OSGi Service Platform Enterprise Specification, but mind it's for V5 release).

With the specification, you declare dependencies of a bundle using separate xml file(s) - OSGI-INF/blueprint/*.xml (the default, but can be changed with the Bundle-Blueprint header).

So, in your case you'll develop the Comm Service bundle whose service is a dependency of the Communication bundle. There are also the Poll and potentially the Postcard bundles of the same service interface that register the services so that they can become dependencies of the Communication bundle.

In such configuration, it's not the Communication bundle to pass the Comm Service bundle/service, but during initialization the Poll and Postcard bundles get their Comm Service service dependency.

Here's a hypothetical Blueprint configuration for the Poll bundle.

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
  <bean id="PollSenderBean" class="pl.japila.osgi.poll.PollSender">
    <argument ref="commService" />
  </bean>
  <service id="PollSenderBeanService" ref="PollSenderBean" 
           interface="pl.japila.osgi.Sender">
    <service-properties>
      <entry key="type" value="poll" />
    </service-properties>
  </service>
  <reference id="commService" interface="pl.japila.osgi.comm.CommService" />
</blueprint>

The Postcard bundle would be similar. Note the entry with the value of poll so you can get a specific service if there's a need for such queries.

A Blueprint configuration for the Communication bundle would be as follows:

<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
  <reference-list activation="lazy" member-type="service-object"
                  id="Sender" interface="pl.japila.osgi.Sender" />
</blueprint>

Refer to the specification and perhaps Apache Aries Blueprint module to study and learn the solution.

Unless you have some need of using a different CommService in different invocations of p.sendPoll(String pollQuestion..) I would say, let PollBundle find the CommService on its own.

Basically this reduces the coupling. You only need to know about PollBundle to send a poll. This really depends on your architecture, but in general, the less objects you need to move around and the simpler your Interfaces, the better.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top