I'm currently trying to get a simple bundle containing a Service Factory running.
This is my factory class:
public class SvcFactory implements ServiceFactory<ServiceB> {
@Override
public ServiceB getService(Bundle bundle,
ServiceRegistration<ServiceB> registration) {
return new ServiceBImpl();
}
@Override
public void ungetService(Bundle bundle, ServiceRegistration<ServiceB> registration,
ServiceB service) {
}
}
This is my service that should be created by the factory:
public class ServiceBImpl implements ServiceB {
private ServiceA svcA;
public void setA(ServiceA a) {
svcA = a;
}
}
And finally the OSGI-INF/component.xml
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="bundleb.internal.SvcFactory">
<implementation class="bundleb.internal.SvcFactory"/>
<reference bind="setA" cardinality="1..1" interface="bundlea.ServiceA" name="ServiceA" policy="static"/>
<service servicefactory="true">
<provide interface="bundleb.ServiceB"/>
</service>
</scr:component>
If I run my test bundles (A, B and C) within equinox I'm getting the following error:
org.osgi.framework.ServiceException: org.eclipse.equinox.internal.ds.FactoryReg.getService() returned a service object that is not an instance of the service class bundleb.ServiceB
I can't find much information about using ServiceFeactories declared in a component definition on the internet. Even the book "OSGi and Equinox" didn't tell me much about using them. Could anyone please explain to me what I'm doing wrong?
ServiceFactory
allows your code to provide customized service object for different bundles. Note that withServiceFactory
, clients of your service still do not control when new instance is created, they lookup service by its interface (ServiceB
) as usual. So, for them, there is no difference if your service is registered asServiceFactory
or not.With declarative services, you should not implement
ServiceFactory
yourself. Just addservicefactory="true"
attribute to the<service>
element (you already did) and different instances of your component class will be created (activated) automatically for different requesting bundles. You need to specifyServiceBImpl
as an implementation class of the component.It is actually rather simple ... DS creates an instance for each bundle, so with DS you do not implement Service Factory, DS does all the hard work. For example:
Every time another bundle gets this XyzService, DS will create a new instance. You can use the ComponentContext (optionally passed in the activate method) to get the bundle that is using you.
Here's an example using ComponentFactory which should fit your needs (and contains a simple integration test to aid with your other question). Disclaimer; the code isn't well written, just for example's sake.
Some service interfaces:
And:
Then the implementations:
}
And:
An integration test that drives the whole thing (note; there's a main method so you run it while start/stopping bundles etc).
And here's the maven pom:
A couple of things to note; I like my integration tests inside the module they test, that way mvn clean install deploy fails if my integration test does - but it's common to see projects with a single integration module for all integration tests. This explains the ugly method
findFileInCurrentDirectoryAndBelow(Pattern pattern)
which is used to locate the current module's bundle in the target directory, and also explains the non-standard setup of the maven-bundle-plugin and maven-scr-plugin plugins.Also the way Pax-Exam picks up the dependencies requires you run the maven build for every change in dependencies and config (e.g. bundle imports/exports, DS changes). But once this is done you can run/debug the tests from Eclipse.
I've put the project in a tarball here
HTH =)