« Back

BND Instruction To Avoid

Technical Blogs April 9, 2018 By David H Nebinger

Introduction

Recently I was building a fragment bundle to expose a private package per my blog entry, . In the original bnd.bnd file, I found the following:

-dsannotations-options: inherit

Not seeing this before, I had to do some research...

Inheriting References

So I think I just gave it away.

When you add the instruction to your bnd.bnd file, the class heirarchy is searched and all @Reference annotations on parent classes will be processed as if they were defined in the base class.

Normally, if you have a Foo class with an @Reference and a child Bar class, the parents references are not handled by OSGi. Instead, you need to add an @Reference annotation to the Bar class and have it call the super classes setter method (it is also why you should always use your @Reference annotations on protected setters instead of private members, because a subclass may need to set the value).

Once you add the dsannotations instruction to your bnd.bnd file, you no longer have to copy all of those @Reference annotations into the subclasses.

My first thought was that this was cool, this would save me from so much @Reference copying. Surely it would be an instruction I'd want to use like all of the time...

Avoid This Instruction

Further research led me to a discussion about supporting @Reference in inheritance found here: https://groups.google.com/forum/#!topic/bndtools-users/6oKC2e-24_E

It turns out that this can be a rather nasty implementation issue. Mainly if you split Foo and Bar to different bundles, the contexts are different. So when processing Bar in a different bundle, it has its own context, class loader, etc from the bundle that has the Foo parent class. I know that OSGi appears to be magic in how it is able to apparently cross these contexts without us as developers realizing how, but there's actually some complicated stuff going on under the hood, stuff that you and I really don't want to know too much about.

But for us to correctly and effectively use the dsannotations inheritance, we would have to know a lot more about how this context stuff worked.

Effectively, it's a can of worms, one that you really don't want to rip the lid off of.

So we need to avoid using this instruction, if for that reason alone.

A more complete response, though, comes from Felix Meschberger:

You might be pleased to hear that at the Apache Felix project we once had this feature in our annotations. From that we tried to standardize it actually.

The problem, though, is that we get a nasty coupling issue here between two implementation classes across bundle boundaries and we cannot express this dependency properly using Import-Package or Require-Capability headers.

Some problems springing to mind:

  • Generally you want to make bind/unbind methods private. Would it be ok for SCR to call the private bind method on the base class ?(It can technically be done, but would it be ok).

  • What if we have private methods but the implementor decides to change the name of the private methods — after all they are private and not part of the API surface. The consumer will fail as the bind/unbind methods are listed in the descriptors provided by/for the extension class and they still name the old method names.

  • If we don’t support private method names for that we would require these bind/unbind methods to be protected or public. And thus we force implementation-detail methods to become part of the API. Not very nice IMHO.

  • Note: package private methods don’t work as two different bundles should not share the same package with different contents.

We argued back then that it would be ok-ish to have such inheritance within a single bundle but came to the conclusion that this limitation, the explanations around it, etc. would not be worth the effort. So we dropped the feature again from the roadmap.

If I Shouldn't Use It, Why Is Liferay?

Hey, I had the same question!

It all comes down to the Liferay code base. Even though it is now OSGi-ified code, it still has a solid connection to the historical versions of the code. Blogs, for example, are now done via OSGi modules, but a large part of the code closely resembles code from the 6.x line.

The legacy Liferay code base heavily uses inheritance in addition to composition. Even for the newer Liferay implementation, there is still the heavy reliance on inheritance.

The optimal pattern for OSGi is one of composition and lighter inheritance; it's what makes the OSGi Declarative Services so powerful, I can define a new component with a higher service ranking to replace an existing component, I can wire together components to compose a dynamic solution.

Liferay's heavy use of inheritance, though, means there's a lot of parent classes that would require a heck of a lot of child class @Reference annotation copying in order to complete injection in the class heirarchy.

While there are plans to rework the code to transition to more composition and less inheritance, this will take some time to complete. Instead of forcing those changes right away and to eliminate the @Reference annotation copying, they have used the -dsannotations-options instruction to force the @Reference annotation processing in the class heirarchy. Generally this is not a problem because the inheritance is typically restricted within a single bundle, so the context change issues are not a problem, although the remainder of the points Felix raised are still a concern.

Conclusion

So now you know as much as I do about the -dsannotations-options BND instruction, why you'll see it in Liferay bundles, but more importantly why you shouldn't be using it in your own projects.

And if you are mucking with Liferay bundles, if you see the -dsannotations-options instruction, you'll now know why it is there and why you need to keep it around.

No comments yet. Be the first.