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:
REngineProvider
bundles. Client code will just use another provider instead.REngine
instances per client bundle.REngine
instancesREngine
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.
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 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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With