4

Im currently trying to setup my own implementation of a ManagedServiceFactory. Here is what I'm trying to do: I need multiple instances of some service on a per-configuration base. With DS the components worked perfectly but now I found out that these services should handle there own lifecycle (i.e. (de)registration at the service registry) depending on the availability of some external resource, which is impossible with DS.

Thus my idea was to create a ManagedServiceFactory, which then would receive configs from the ConfigurationAdmin and create instances of my class. These again would try to connect to the resource in a seperate thread and register themselves as service when they're ready to operate.

Since I had no luck implementing this yet, I tried to break everything down to the most basic parts, not even dealing with the dynamic (de)registration, just trying to get the ManagedServiceFacotry to work:

package my.project.factory;

import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedServiceFactory;

public class Factory implements BundleActivator, ManagedServiceFactory {

private ServiceRegistration myReg;
private BundleContext ctx;
private Map<String, ServiceRegistration> services;

@Override
public void start(BundleContext context) throws Exception {
    System.out.println("starting factory...");
    this.ctx = context;
    java.util.Dictionary properties = new Hashtable<String, Object>();
    properties.put(Constants.SERVICE_PID, "my.project.servicefactory");
    myReg = context.registerService(ManagedServiceFactory.class, this,
            properties);
    System.out.println("registered as ManagedServiceFactory");
    services = new HashMap<String, ServiceRegistration>();
}

@Override
public void stop(BundleContext context) throws Exception {
    for(ServiceRegistration reg : services.values()) {
        System.out.println("deregister " + reg);
        reg.unregister();
    }
    if(myReg != null) {
        myReg.unregister();
    } else {
        System.out.println("my service registration as already null " +
                           "(although it shouldn't)!");
    }
}

@Override
public String getName() {
    System.out.println("returning facotry name");
    return "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, ctx.registerService(ServiceInterface.class,
                new Service(), properties));
    } else {
        // i should do some update here
    }
}

@Override
public void deleted(String pid) {
    ServiceRegistration reg = services.get(pid);
    if (reg != null) {
        reg.unregister();
    }
}

}

Now, it should receive configurations from the ConfigurationAdmin for PID my.project.servicefactory, shouldn't it? But it does not receive any configurations from the ConfigurationAdmin. The bundle is started, the service is registered and in the web console, I can see the config admin holds a reference to my ManagedServiceFactory. Is there a certain property which should be set? The interface specification does not suggest that. Actually my implementation is more or less the same as the example there. I've no idea what I'm doing wrong here, any pointers to the solutions are very welcome.

Also, I orginally thought to implement the ManagedServiceFactory itself as DS, which also should be possible, but I failed at the same point: no configurations are handed over by the ConfigAdmin.

update To clarify the question: I think that this is mainly an configuration problem. As I see it, I should be able to specify two PIDs for the factory, one which identifies a configuration for the factory itself (if any), and one which would produce services trough this factory, which I thought should be the factory.pid. But the framework constants do not hold anything like this.

update 2 After searching a bit the Felix Fileinstall source code, I found out that it treats configuration files differently when there is a - in the filename or not. Having the configuration file named my.project.servicefactory.cfg it did not work, but the configs named my.project.servicefactory-foo.cfg and my.project.servicefactory-bar.cfg were properly handed over to my ManagedServiceFactory as expected, and multiple services with ServiceInterface were registered. Hurray!

update 3 As proposed by Neil, I put the declarative service part in a new question to bound the scope of this one.

Community
  • 1
  • 1
benjamin
  • 1,066
  • 11
  • 22
  • Though not answering your question directly, the last time I wrestled with `ManagedServiceFactory` I took Neil Bartlett's advice and used DS components instead, as mentioned here: http://stackoverflow.com/a/4129464/31818 – seh Aug 06 '12 at 17:27
  • As I mentioned above, DS will does not work for components (services) that need to manage their own lifecycle. DS gives you no means to control the service (un)registering process but instead manages the lifecycle for you (which is, in most cases, a positive thing!) – benjamin Aug 06 '12 at 17:40
  • Ah, I had missed that requirement when reading your question earlier. It sounds, then, like the presence of your services is contingent on two things: a configuration being assigned, *and* some external resource being available. I will trust that you know your requirements better than any of the rest of us. – seh Aug 06 '12 at 19:29
  • 1
    Two questions: (1) Does Config Admin actually have a configuration record with a PID that matches your MSF? (2) What is `ServiceConstants.PID`, is this a constant you have defined yourself? Since you have not listed it explicitly I cannot verify that it is correct... why not use `Constants.SERVICE_PID` from the `org.osgi.framework` package? – Neil Bartlett Aug 06 '12 at 22:02
  • 1) Yes, the CA has a config for pid my.project.servicefactory. 2) ServiceConstants was something from Pax, that was wrong, I updated it above to Constants.SERVICE_PID, but without any effect – benjamin Aug 07 '12 at 06:43
  • I read your second update. I can help you but this thread is getting really confusing. PLEASE post the question about DS separately. – Neil Bartlett Aug 07 '12 at 15:58

1 Answers1

4

I think that the problem is you have a singleton configuration record rather than a factory record. You need to call Config Admin with the createFactoryConfiguration method using my.project.servicefactory as the factoryPid.

If you are using Apache Felix FileInstall (which is a nice easy way to create config records without writing code) then you need to create a file called my.project.servicefactory-1.cfg in the load directory. You can create further configurations with the same factoryPID by calling them my.project.servicefactory-2.cfg etc.

Neil Bartlett
  • 23,743
  • 4
  • 44
  • 77
  • Exactly, this was the problem. I just updated my own post while you were answering, because I'd rather like to solve this with DS. Thanks! – benjamin Aug 07 '12 at 08:43
  • I think you should open a new question on StackOverflow and try to keep the details of what you're trying to do with DS separate from the MSF stuff. Otherwise this thread will just get really confusing. – Neil Bartlett Aug 07 '12 at 10:03