Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OSGi services architecture: creation of service at request of consumer

I am developing an application in Eclipse RCP. I need help with a design decision concerning the design of a service.

I have some bundles which are used to provide an REngine object to other modules. The REngine is an interface to a calculation engine, which can be implemented in multiple ways. The bundles provide instances of REngine by connecting to a remote server or starting a local calculation thread. Some bundles require configuration by the GUI (but also need to be available on a headless platform). A client Bundle may request multiple REngine objects for parallel calculations.

I currently register these modules to provide an REngine service. The service is created by a ServiceFactory, which either launches a local calculation instance or a remote (server) instance. The client is responsible for trying out all Service registrations of the REngine class and selecting the right one.

The code to do this can be summarised as follows:

class API.REngine { ... }

class REngineProvider.Activator {
    public void start(BundleContext ctx) {
      ctx.registerService(REngine.class.getName(), new REngineFactory(), null);
    }
}
class REngineProvider.REngineFactory implements ServiceFactory {
    public Object getService(Bundle bundle, ServiceReference reference) {
      return new MyREngineImplementation();
    }
    public void ungetService(REngine service) {
       service.releaseAssociatedResources();
    }
}

class RConsumer.Class {
    REngine getREngine() {
        ServiceReference[] references = bundleContext.getAllServiceReferences(REngine.class.getName(), null);
        for(ServiceReference ref: references) {
            try {
            return bundleContext.getService(ref);
            } catch (Exception e) {} // too bad, try the next one
        }
    }
}

I would like to keep this model. It is nice that the OSGi service spec matches my business requirement that REngine objects are living objects which should be released when they are not needed anymore.

However, a registered service can only provide one service instance per bundle. The second time the service is requested, a cached instance is returned (instead of creating a new one). This does not match my requirement; a bundle should be able to get multiple REngine objects from the same provider.

I have already looked at other OSGi framework classes, but nothing seems to help. The alternative is the whiteboard model, but it seems strange to register an REngineRequestService that is used by the REngineProvider bundle to give out a live REngine.

How do I implement this in OSGi? As a reminder, here is my list of requirements:

  1. Easy enabling and disabling of REngineProvider bundles. Client code will just use another provider instead.
  2. Configuration of REngineProvider bundles.
  3. Multiple REngine instances per client bundle.
  4. Explicit release of REngine instances
  5. REngine creation can fail. The client module should be able to know the reason why.

Just to add the solution I have chosen as future reference. It seems the OSGi Services platform is not made for "requesting a service". It is the provider bundle that creates a service, and the client bundle that can find and use the services. It is not possible to provide an automatic "Factory" for services per user request.

The solution chosen involves the OSGi whiteboard model. On first sight, this may seem very difficult to manage, but Blueprint can help a lot!

Provider blueprint.xml file:

<reference-list interface="org.application.REngineRequest"
          availability="optional">
  <reference-listener 
          bind-method="bind" unbind-method="unbind">
      <bean class="org.provider.REngineProvider"/>        
  </reference-listener>

The class REngineRequest is a shared API class allowing the provider to input his REngine object, or set an Exception explaining why the creation did not work.

For the client, using an REngine is now as easy as doing:

REngineRequest req = new REngineRequest();
ServiceRegistration reg = bundleContext.registerService(req, REngineRequest.class.getName(), engineCreationProperties);
req.getEngine().doSomeStuff();
reg.unregister();

We make the assumption that the provider will never stop while the client is using the REngine. If it does, the REngine becomes invalid.

like image 485
parasietje Avatar asked Mar 28 '12 11:03

parasietje


People also ask

What are OSGi services?

The OSGi Service Platform provides a mechanism for developing applications by using a component model and deploying those applications into an OSGi framework. The OSGi architecture is separated into a number of layers that provide benefits to creating and managing Java™ applications.

How does OSGi framework work?

How does OSGi work? OSGi is a set of specifications that define a dynamic component system for Java. These specifications allow for a development model in which an application is composed of several components, and then packed into bundles. These components communicate locally and through the network via services.

What is OSGi service in AEM?

An OSGi service is a Java class or service interface, along with a number of service properties as name/value pairs. The service properties differentiate among different service providers that provide services with the same service interface.

What are OSGi components?

OSGi is a set of specifications. Its core specification defines a component and service model for Java. A software component in OSGi is called bundle or plug-in, both terms are interchangeable. Services are Java implementations which OSGi allows to start and access.


2 Answers

ComponentFactory from the Declarative Services is what you need. Most of the time you should rather use DS instead of manually registering and looking up the services.

The provider side should register REngine factory service (you don't have to implement the factory itself, DS will do that for you). The consmer should declare one-to-many dependency to the REngine service. At runtime, all available factories will be injected and consumer can go through them to create actual REngine instances.

like image 140
Ivan Dubrov Avatar answered Nov 15 '22 04:11

Ivan Dubrov


Two years ago I tried to create Genuine Service Factories what later became Parameterized Services. However, after analysis it turned out that nothing was needed, just register the factory as the service.

However.

I do not know enough about your service but it sounds very much that you could significantly simplify things by removing control from the client bundle, the client bundle should just use whatever REngine service is available in the service registry, maybe with a property signalling its use type if there are multiple bundles that need REngines and they should not share the same REngine (which should rarely be the case).

If that model is possible, it usually significantly simplifies. I generally then use DS with Configuration Admin configurations that drive the instances (one of the most useful aspects of DS, see http://www.aqute.biz/Bnd/Components). With the metatype integration, you even get a user interface to edit your configuration properties.

like image 38
Peter Kriens Avatar answered Nov 15 '22 04:11

Peter Kriens