Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 330866

Summary: OSGi integration story: injecting the proper bundle context
Product: z_Archived Reporter: Simon Chemouil <eclipse>
Component: E4Assignee: Project Inbox <e4.runtime-inbox>
Status: CLOSED DUPLICATE QA Contact:
Severity: normal    
Priority: P3 CC: bokowski, contact, eclipse, erdal.karaca.de, jawr, jeffmcaffer, john.arthorne, Lars.Vogel, ob1.eclipse, pwebster, tjwatson
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
Whiteboard:

Description Simon Chemouil CLA 2010-11-22 17:15:16 EST
================================================================
(1) Having a way to get the bundle context injected in user code
================================================================

The main discussion here is to decide how it should work. Using stock injection on model elements, DI will first work with an EclipseContext, which means an injection is equal to doing eclipseContext#get(). This does not work with BundleContext because the EclipseContext is mapped with Model Elements and the way the bundles are "modularized" may not be symmetric. 

There are two potential solutions:
#1 providing a generic extension to EclipseContexts so that they can store metadata not directly related with their hierarchy. This implies changing API in core.context to do something more general than the actual need. I'm not a fan of it
#2 not have the bundle context in the EclipseContext at all. It couldn' be supplied by the OSGiContextStrategy because the OSGiContextStrategy is linked to the root context, and returns values to the context and not to the IRequestor directly... It would support only one BundleContext for the whole application ;). One solution would be to "hack" it using @Named the symbolic-name of the requesting bundle, but this is ugly. The best solution would to have an ExtendedObjectSupplier doing it...

I would go for solution #2, and here are several ways of doing it.

Currently, I think the ExtendedObjectSupplier is the best and least "hackish" approach, even if it saddens me that this would take place before an EclipseContext lookup (if we ask @Inject BundleContext, we pretty much know what we want, we shouldn't have to look through the context first, and it shouldn't be able to "break" it by setting an object with the key BundleContext.class.getName() in a context).

Another approach to do this would be to change ContextObjectSupplier so that if the descriptor is BundleContext.class.getName(), it would do the proper logic to find the bundlecontext immediately. There's no need to track BundleContext because they never change during a bundle's lifetime.

My preferred approach is to refactor the whole primary/extended object supplier system so that there would only be ObjectSuppliers, some of which would be chained "by default" by an injection factory, others that would be added dynamically if they register a service (like ExtendedObjectSuppliers do now). In that case, the ContextInjectionFactory would create an "injector chain" starting with the OSGiObjectSupplier then the ContextObjectSupplier. Other extensions would be provided as currently.


Question: how to get the bundle context?

I've let that question aside for now because the architecture is not very dependant of "how" we get the actual bundleContext. In general, I would generally prefer to avoid calling FrameworkUtil#getBundle() or getting it through PackageAdmin#getBundles() but I believe this is impossible.. 

As far as I know , there should be a clean way to do it: the Equinox Extension Registry works as an 'extender bundle' which listens for all bundle events before trying to read "plugin.xml". Declarative Services provides the bundleContext to its managed components this way, but the Equinox Extension Registry does not keep the bundle (instead, it puts the bundle symbolic name in the IContributor object, and we can query ContributorFactoryOSGi#resolve to find the bundle ; it works since 'eclipse plugins' must be singletons).

I think FrameworkUtil#getBundle() is quicker on Equinox.



What do you think? Comments, idea, welcome. I'd be glad to start hacking away once I know I have the green light (of course, after that there's still review and discussion, but I don't have enough free time to go in a wrong direction).
Comment 1 Simon Chemouil CLA 2010-11-22 17:20:27 EST
This bug is step 1 for bug 330865
Comment 2 Jeff McAffer CLA 2010-11-22 20:49:35 EST
When I do talks on OSGi best practices the #1 is don't program OSGi API so I think making the use of BundleContext easier is a BAD thing.  I would hope that the e4 programming model makes it largely irrelevant whether or not OSGi is being used. Perhaps some usecases or examples of when a BundleContext would be needed will help shed some light on the best way (or if at all) to make it available.
Comment 3 Simon Chemouil CLA 2010-11-23 07:33:52 EST
(In reply to comment #2)
> When I do talks on OSGi best practices the #1 is don't program OSGi API so I
> think making the use of BundleContext easier is a BAD thing.  I would hope that
> the e4 programming model makes it largely irrelevant whether or not OSGi is
> being used. Perhaps some usecases or examples of when a BundleContext would be
> needed will help shed some light on the best way (or if at all) to make it
> available.

Okay, I believe there are two layers of "best practices". Best practices when programming against the OSGi API (don't pass references to services to another bundle, don't call the framework with a lock, avoid statics, etc), that I would call "OSGi best practices".. And best practices for a general point of view to avoid a dependence on OSGi and have thread-safe components when using a dynamic mode but that's more generally "program using POJOs" and this is also the mantra of E4 and other DI frameworks.

Whether we like it or not, sometimes we're forced to program with the OSGi API. Two simple examples would be to have a service tracker with a customizer or a filter, which can't be done using E4's DI, or publishing an EventHandler with a dynamic topic (which can't be done using E4's DI either). 

As a framework, the Eclipse 4 Application Platform will only provide a subset of the capabilities of OSGi (through DI). In fact, that will itself be a subset of what DS provides. This is for several reasons that have been explained by mail on e4-dev@ but the gist is that some use cases don't make sense with E4 managed objects (for instance, large parts of the "static" DS 'mode' can't really work, e.g static 0..1 because we can't dispose & create the view again when an optional service goes).

I have come across several patterns in DS when it's useful to get the BundleContext (or ComponentContext#getUsingBundle()) from the activate method. I have come across the very same situation while programming an E4 RCP application. Trust me that we do our best (and generally succeed) to avoid any dependency or programming directly against the OSGi API, but this is sometimes impossible.

We need a way for powerusers to access interact with other bundles cleanly for those use cases that aren't supported by DI.  Also we can't ask those users to change the way their services are accessed (publish them under different interfaces or whatever) because those services might come from 3rd party bundle.

After a first discussion the decision was not to *change* E4's DI system on that regard (maybe add filtering/etc but it won't be dynamic so it won't ever be enough). We believe it addresses 90% use cases, just like DS address 95% uses cases.

Having the proper BundleContext handy is just the first part of the next step which is to be able to register services on the proper bundlecontext. If we have it handy for that, we should also allow it to be injected.
Comment 4 Jeff McAffer CLA 2010-11-23 10:50:48 EST
I've not been involved much in the e4 DI stuff so don't know all the ins and outs so take my comments as you choose.

We are in agreement that sometimes there is the need to look under the covers.  A simplistic view says that if lots of people need to look under the covers (e4 DI) then the covers are not doing their job and should be fixed.  If few people need to look under, then tuck them in and make it hard to look under so people don't do so by accident or through laziness.
Comment 5 Simon Chemouil CLA 2010-11-23 12:03:07 EST
(In reply to comment #4)
> I've not been involved much in the e4 DI stuff so don't know all the ins and
> outs so take my comments as you choose.
> 
> We are in agreement that sometimes there is the need to look under the covers. 
> A simplistic view says that if lots of people need to look under the covers (e4
> DI) then the covers are not doing their job and should be fixed.  If few people
> need to look under, then tuck them in and make it hard to look under so people
> don't do so by accident or through laziness.

Ok, I'm not sure if I was clear on purpose here then ;).

It would allow to get the proper BundleContext injected, for instance, the user would have to write:

----------------->8--------------------
@Inject BundleContext bundleContext;
----------------->8--------------------

in the fields

or a maybe safer way

----------------->8--------------------
@PostConstruct
public void activate(BundleContext bundleContext, SomeService service, MPart theActivePart) {
  // do something
}
----------------->8--------------------

This second way is really similar to what we can do in DS and have the BundleContext or ComponentContext injected there but it's totally optional for users who need to tinker a bit.

This is not something that happens unless the user wants it. E4's DI is really an implementation of JSR-330 so the behavior here is the same as with other DI frameworks that implement it such as Google Guice.

This is step one of my "plan of action" because we need to bind the ServiceReferences to the proper BundleContext. That's step 2.


Note that it is/will be still *immensely* easier to get an OSGi service completely transparently through injection, just ask for it and it's here. That covers most of the use cases for UI elements.

But having the BundleContext handy also means we can give it to the users who want it, maybe they get it injected only in OSGi-dependant parts of their implementation, it's up to them to design appropriately if they don't want to be tied to the OSGi API too much. But for those who want it, it's far better than using a static method (even if we're just hiding it). A lot of RCP 3.x users resort to using FrameworkUtil#getBundle because of the lack of out-of-the-box integration between 3.x managed objects such as views and OSGi services. We could propose a cleaner approach here :).
Comment 6 Oleg Besedin CLA 2010-11-25 16:45:42 EST
In an arbitrary place the bundle context can be obtained using this single line of code:

BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();

IMO, BundleContext should not be placed in the context as the context tree structure is ortogonal to bundles.
Comment 7 Simon Chemouil CLA 2010-11-25 18:04:30 EST
(In reply to comment #6)
> In an arbitrary place the bundle context can be obtained using this single line
> of code:
> 
> BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
> 
> IMO, BundleContext should not be placed in the context as the context tree
> structure is ortogonal to bundles.


Hi Oleg,

Thanks for your reply.

Indeed, I forgot to add that line here as well, but in the linked bug, here's my original description and reason for opening this bug:

> (1) Having a way to get the bundle context injected in user code (people 
> should not be forced to call FrameworkUtil#getBundle() or have an activator
> with a static bundleContext reference)

My reasons is to avoid forcing the users to use statics/singletons in their code, and *mostly* it doesn't 'feel' right when we have an injection framework. This is something we definitely missed when developing our RCP app with E4. As a temporary solution, we even created some injected objects in non-E4 managed bundles with a ContextInjectionFactory (so the context was not shared outside) and first thing we did was to put the BundleContext there :).

I believe there were also talks in the OSGi Alliance of removing FrameworkUtil to allow nested framework instances (but that was in a draft).

I do agree, though, that the BundleContext has no place in the context, but the injector can have other sources.
Comment 8 Lars Vogel CLA 2014-01-17 10:18:18 EST
Fixed with https://git.eclipse.org/c/platform/eclipse.platform.runtime.git/commit/?id=a264facfd581f2b16f348d9247e94cb39c0b8e96

*** This bug has been marked as a duplicate of bug 423212 ***