How to implement ManagedServiceFactory as a Decalarative Service in OSGi?

I have services that need to be created based on each configuration, each of which relies on an external resource and, therefore, must manage its own life cycle (i.e. (de) register the service). Thus, implementing this data as a DS and including SCR in multiple instances does not work.

You can implement a package that registers a ManagedServiceFactory to perform this task (see my previous post ). But, as a result, if the factory depends on several other services, you need to start tracking these services and write a lot of glue code so that everything works. Instead, I would like to implement a declarative factory as (singleton) service for which SCR registers a ManagedServiceFactory in the services registry.

Here is my attempt:

 import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedServiceFactory; import org.osgi.service.component.ComponentContext; @SuppressWarnings({ "rawtypes", "unchecked" }) public class Factory implements ManagedServiceFactory { private BundleContext bundleCtxt; private Map<String, ServiceRegistration> services; public void activate(ComponentContext context) throws Exception { System.out.println("actiavting..."); this.bundleCtxt = context.getBundleContext(); services = new HashMap<String, ServiceRegistration>(); } public void deactivate(ComponentContext context) { for(ServiceRegistration reg : services.values()) { System.out.println("deregister " + reg); reg.unregister(); } services.clear(); } @Override public String getName() { System.out.println("returning factory name"); return "my.project.servicefactory"; } @Override public void updated(String pid, Dictionary properties) throws ConfigurationException { System.out.println("retrieved update for pid " + pid); ServiceRegistration reg = services.get(pid); if (reg == null) { services.put(pid, bundleCtxt.registerService(ServiceInterface.class, new Service(), properties)); } else { // i should to some update here } } @Override public void deleted(String pid) { ServiceRegistration reg = services.get(pid); if (reg != null) { reg.unregister(); services.remove(pid); } } } 

and service description:

 <?xml version="1.0" encoding="UTF-8"?> <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" configuration-policy="ignore" name="my.project.servicefactory"> <implementation class="my.project.factory.Factory"/> <service> <provide interface="org.osgi.service.cm.ManagedServiceFactory"/> </service> <property name="service.pid" type="String" value="my.project.servicefactory"/> </scr:component> 

I already found out that the β€œfactory” property in the service description is the wrong way, since this component is never registered as a ManagedServiceFactory in the services registry, instead it becomes a ComponentFactory .

As a kind of hack, I just added a component property, namely

 <property name="service.pid" type="String" value="my.project.servicefactory"/> 

and added configuration-policy="ignore" . This works: configurations named my.project.servicefactory-foobar.cfg are passed to my service, which registers them as in the services registry, everything is in order.

But here are two things I don’t like about it:

  • manually setting the service.pid property looks like a dirty hack for me.
  • configuration-policy="ignore" does not allow me to configure a ManagedServiceFactory . If I leave this property or set it, I would get one ManagedServiceFactory for the configuration named my.project.servicefactory.cfg , and then two services for each configuration named with the template my.project.servicefactory-foobar.cfg : one ManagedServiceFactory which appears in SCR and one ServiceInterface that my first ManagedServiceFactory registered when it receives a notification about this new configuration. (At least this does not grow exponentially because SCR overwrites the service.pid property for factory configurations)

So how do I configure this correctly?

PS: For those who are wondering about my configuration link by their file names: I use Felix Fileinstall for configurations, so foo.cfg is placed in ConfigAdmin for PID foo , and foo-bar.cfg is placed there for factory-pid foo .

+2
source share
2 answers

Just use your DS instance without a header, properties, and register the service yourself:

 @Component(immedate=true, provide={}, serviceFactory=true, configurationPolicy=require) public class Mine { BundleContext context; volatile ServiceRegistration r; @Activate void activate(BundleContext context, Map<String,Object> map) { this.context = context; track(map); } @Deactivate void deactivate() { if ( r != null) r.unregisterService(); } void track(Map<String,Object> map) { ... // do your remote stuff r = context.registerService(...); ... } } 
+3
source

Why doesn't DS support work for you? See 112.6:

Factory Configuration – If a factory PID exists, with zero or more Configurations, that is equal to the configuration PID, then for each Configuration, a component configuration must be created that will obtain additional component properties from Configuration Admin.

This suggests that if the pid configuration of your component is the same as the factory pid in CM, then DS will create an instance of your component for each configuration under the factory pid.

+2
source

All Articles