Can Bnd generate a "factory" service like LoggerFactory?

Yes it does. You are correct. In reality, you do not need to register a BongoFactory service as no one will use that service and you really do not want anyone actually using the component other than the OSGi framework. You should just make it an immediate component.

@Component(immediate=true, service={})
class BongoFactory ...

In order to tell the resolver that someone provides the Bongo service, decorate the immediate component with service capability (use the proper fully qualified name for the Bongo class).

@Capability(namespace = ServiceNamespace.SERVICE_NAMESPACE,
    attribute = {
	ServiceNamespace.CAPABILITY_OBJECTCLASS_ATTRIBUTE +
            ":List<String>=org.bongo.Bongo" },
    uses = Bongo.class,
    effective = Namespace.EFFECTIVE_ACTIVE)
@Component(immediate=true, service={})
class BongoFactory ...

Then you do not need the fake component.

Only if all the constructor arguments are already services. That may not be appropriate.

I think using an immediate component to register the service using a service factory and decorating it with the service capability is the proper solution here.

Ah, thanks. I think this will also resolve my other worry that the framework would be unaware that the Bongo service “depends” on the BongoFactory component.

Cheers,
Chris

Here is a more complete example [updated]:

package com.acme;
import org.osgi.framework.Bundle;
public class Acme {
	private final Bundle bundle;
	public Acme(Bundle bundle) {
		this.bundle = bundle;
	}
	public void doSomething() {
		bundle.getBundleContext();
	}
	public void close() {
		// any close actions
	}
}
package com.acme.impl;
import org.osgi.annotation.bundle.Capability;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.namespace.service.ServiceNamespace;
import org.osgi.resource.Namespace;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import com.acme.Acme;
@Capability(namespace = ServiceNamespace.SERVICE_NAMESPACE, attribute = {
		ServiceNamespace.CAPABILITY_OBJECTCLASS_ATTRIBUTE
				+ ":List<String>=com.acme.Acme"
}, uses = Acme.class, effective = Namespace.EFFECTIVE_ACTIVE)
@Component(immediate = true)
public class AcmeServiceFactory {
	final ServiceRegistration<Acme> registration;
	@Activate
	public AcmeServiceFactory(BundleContext context) {
		registration = context.registerService(Acme.class,
				new ServiceFactory<Acme>() {
					@Override
					public Acme getService(Bundle bundle,
							ServiceRegistration<Acme> reg) {
						return new Acme(bundle);
					}
					@Override
					public void ungetService(Bundle bundle,
							ServiceRegistration<Acme> reg,
							Acme service) {
						service.close();
					}
				}, null);
	}
	@Deactivate
	public void deactivate() {
		registration.unregister();
	}
}

I think this can be considered the proper way to do a ServiceFactory, where the service objects need complex construction, in DS.

2 Likes

Oh my! That’s very interesting! It didn’t occur to me to add the Bundle to the Bongo constructor.

Thanks again,
Chris

Thank you kindly for all who contributed to this thread. I’ve managed to adapt @bjhargrave 's example to my use case (though I used a PrototypeServiceFactory rather than a plain ServiceFactory). This has noteably simplified the client-side of my code.