1
votes

I'm trying to understand how to control dependency injection in OSGi (specifically Apache Felix as used in Adobe Experience Manager (AEM)). I have a servlet with an @Reference annotation on a field that references an interface -- in my case, it represents a secure document signing provider. I have an implementation class that implements the interface, and it's automatically injected into the servlet.

In the servlet:

@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private DocumentSigningProvider signingProvider;
...
URL redirectUrl = signingProvider.Sign(...);

and my implementation class:

@Component(metatype=true)
@Service
@Property(name = "service.ranking", intValue = 1000)
public class DocumentSigningProviderDocuSignImpl implements DocumentSigningProvider {

If I write a second implementation, I can control which one is injected via the service.ranking value -- highest number wins. If no ranking is declared on any of the implementations, the oldest wins.

So far, so good -- except that to change the values I need to recompile and redeploy. I need to control this at runtime, or via a configuration file that's tied to the environment or "runmode". I can't see how to do that.

Since the @Component declares metatype=true, @Property annotations within the class generate a control in the OSGi console's GUI. I can use that GUI to change values at runtime. But the service.ranking is declared in an @Property on the class itself, and it doesn't appear to generate a control in the GUI.

In addition, configuration files named after the class provide default values at runtime, and I can have a different config file for each environment or "runmode". This would work for me too; in one environment I can configure a mock implementation, and use a "real" implementation in another environment. But again, these config files seem to work for @Property declarations inside the class, but not on the class.

I've read a number of threads here about this subject but none touch on exposing service.ranking in the OSGi GUI or in config files.

Is there any way to control which class is injected without modifying, re-compiling and re-deploying source code?

1

1 Answers

1
votes

In declarative services you can override any service property at runtime by providing a config for the component and setting the property. So setting service.ranking=1 or similar should actually work.

One thing to note. By default declarative services binds to the first available service and stays with it. So if you want to make sure a service with a higher ranking is used even if it comes later than the one with the lower ranking then you need this option on the consumer side:

@Reference(policyOption=ReferencePolicyOption.GREEDY)

This makes sure DS switches the service if a better one comes later.

If you want to be more specific what service to use on the consumer side you can also use a filter at runtime.

target.signingProvider=(myproperty=myvalue)

I have collected some more hints here.