Expand my Community achievements bar.

How to control OSGi dependency injection in AEM?

Avatar

Level 3

I'm trying to understand how dependency injection works in OSGi (specifically Apache Felix as used in 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

    public class DocumentSigningDocuSignImpl implements DocumentSigningProvider {

    ...

    @Override

    public URL Sign(...) {...}

  

As you can see, in my servlet I code against the interface, not the implementation, which is never mentioned in the servlet.

Question: When I write a 2nd implementation class for the same interface, how do I control which implementation is injected into the servlet?

I'd like to control this via configuration so I can use a different impl class in different run modes. Secondarily, it might be useful to be able to select the impl dynamically at runtime, e.g if I need to select among implementations based on some request parameter.

9 Replies

Avatar

Level 4

I think we can achieve this using a mechanism called "Service Ranking".  By default, which ever implementation got a highest rank that implementation class will be selected. Developer can control which implementation needs to be selected by using OSGI property called SERVICE_RANKING.

refer to this article... i followed this one when i had this question in my previous project....

cq5 - Multiple Interface Implementations - AEM/CQ - OSGi - Stack Overflow

For detailed explanation you can refer to the following helpx article

Adobe Experience Manager Help | Getting Started with Adobe Experience Manager and OSGi bundles

Let me know if you are still running into issues

Avatar

Level 3

Thanks, this looks like the right direction: on each implementation class, add

@Property(name="service.ranking", intValue=<some integer>)

However, to change the order, I need to change code, rebuild and redeploy. When I add the @Property on the class, I don't see this the service.ranking property in the Felix console. is there any way to see and edit that value in configuration, so I can simply change it in the console, inactivate and reactivate the components or bundle, and have the new value take effect, rather than re-deploy?

Avatar

Employee Advisor

If you want to do it yourself, you can use an OSGI service tracker to manage all implementations and then select the implementation you are interested in (by whatever means).

Instead of the "raw" servicetracker SCR offers some nice abstractions for it, mostly the ReferenceCardinality. A nice blog entry for it is Multiple Cardinality OSGI @References Using SCR Annotations | Architect's Log Stardate Today

Jörg

Avatar

Level 3

Jorg, thanks, that's interesting -- but in my case, at least for now, I only want a single implementation at a time. I now understand how the impl is selected -- service.ranking if it exists, or a filter, or if none specified, the oldest impl -- but I'm now trying to see how I can change the service.ranking via configuration so I can change it at runtime or based on runmode. I'd really like not to have to recompile and redeploy to change the resolution order.

Avatar

Employee Advisor

If you want to make it runmode based, I would advise to set the ConfigurationPolicy to REQUIRED; then you can set a proper configuration for a special impl only for the runmode you want it. Or you can make the service ranking a property which you can change via OSGI configuration.

Jörg

Avatar

Level 3

That sounds promising --  I looked at ReferenceCardinality but didn't look at ConfigurationPolicy. And, I was hoping to be able to make the service.ranking a property -- that would generate a Felix GUI for it -- but I've only done that on fields within a class, so I didn't think it could be done. Can you sketch out some example code that would show how I would decorate the class to make service.ranking into a property?

I have to leave for the day -- I'll take a look back here tomorrow. Thanks for the suggestions, folks, this is helping!

Avatar

Employee Advisor

ah, got the service ranking wrong. That's a part of the @Component definition and not a standard property. Sorry.

Jörg

Avatar

Level 3

rmahendr Yes, I've read that article, and the service ranking info tells me how the framework selects one implementation out of many. What it does not address, and what I'm still seeking, is how to change the service ranking value via configuration rather than by changing an annotation in the Java class. Changing the annotation means re-deploying, and I want to avoid that.

From what I can tell, If an @Component has metatype=true, then @Property annotations on fields inside the class generate controls in the Felix GUI -- but @Property annotations on the class itself -- which is where the service.ranking is declared -- do not generate a GUI control.