Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [sisu-users] Problem with the Sisu Extender

On 5 Sep 2013, at 11:28, gian.maria.romanato@xxxxxxxxxxxx wrote:

> If the objects that you are trying to instantiate were defined in B2
> then you should use their defining injector as this would contain
> both their bindings and the necessary bindings to fetch the required
> dependencies from B1's injector. Think of injectors as loosely-
> coupled sources of bindings, with any missing (local) dependencies
> routed through the BeanLocator.


In my humbe opinion this is not that convenient, especially when the extender is used (as it would require a lookup in the OSGi service registry to get the Provider<Injector> instance). But most important, it does not seem to allow an object to be injected an implementation that is provided by a dependant or unrelated bundle. Example: Bundle B1 declares class Bean which uses interface I1 as one of its fields, but B1 has no implementation for I1. Bundle B2 provides Impl1 which is an implementation of I1. B2 may depend on B1 (e.g. I1 is declared by B1 itself) or not (e.g. I1 is declared in bundle B0 known to both B1 and B2). I would like code in B1 to be able to instantiate class Bean and have its field of type I1 injected with the implementation provided by B2.

In Guice once an injector is created its collection of bindings is considered immutable (apart from just-in-time bindings, but they only cover very specific situations). This means an injector cannot itself work as a central service locator/registry where you have bundles coming and going over time. This was a conscious decision by the Guice team, see http://code.google.com/p/google-guice/issues/detail?id=49 and various threads on the Guice user list for more background. There is no way to insert new bindings into an existing injector when a bundle starts, nor any way to remove bindings from the injector when it stops.

Sisu solves this problem by using the Guice SPI to analyze the bindings being supplied to the injector; adding bindings for missing dependencies that delegate to the BeanLocator which can act as a central service locator. So from the perspective of an implementation bound in a given injector it can see all its local dependencies plus any external dependencies. Furthermore resolution of external dependencies is dynamic, meaning that you can swap in alternative implementations while minimizing downtime.

For portability components should try to stick to JSR330 and @Inject their dependencies rather than rely on programmatic lookup; then they don't mind how their dependencies are supplied. For example, if Bean used constructor/field/setter injection to @Inject an implementation of I1 then Sisu would see this during WireModule analysis and provide a binding from I1 to a provider that queries the BeanLocator for implementations of I1. This makes it easier to re-use Bean inside other JSR330-based applications or containers. Knowledge of the surrounding container or central locator should ideally be limited to a small number of components (typlically related to bootstrapping the application).

[ I discuss this approach in a number of presentations at http://www.slideshare.net/mcculls ]

In other words, my hope was that I could use an Injector created with Sisu the same way I can use a BundleContext to obtain a service: it does not matter which bundle registers the service, I can always get it with any BundleContext as long as I know the Java interface.

The issue here is programmatic lookup of arbitrary types - the BeanLocator is best placed to perform this lookup as it has a view over all injectors, whereas each injector can only see its local bindings plus those added during dependency analysis.

I would have liked to bootstrap programmatically only one Injector with Sisu, let the extender create the additional modules, Injectors and Providers of <Injector> and update the BeanLocator, and just use my manually bootstrapped Injector to perform any injection, without worrying about which bundle provides which implementation. More or less the same way you use the Equinox Registry to instantiate extensions: typically you don't really care where they come from, the bundle which declares the extension point can leverage the registry to locate them all and instantiate them because the Registry is one and knows everything.
When using Guice in a JSE application, where all JARs live in the same classloader, you can inject objects with implementations that come from whichever JAR, provided that all modules have been registered. I'd like something similar in OSGi. Wouldn't it be great if Sisu gave me a sort of "composite" Injector, or a facade over the Guice Injector, which leverages the knowledge of the bean locator to support injections using implementations that come from any bundle in the application ?

That's possible; here's some example code from the old codebase that lets you enhance an existing injector:


This was temporarily shelved in the move to Eclipse because it's incomplete and I didn't want to commit to supporting this in the first release - but it should reappear at some point :)

> Also how are you using the injector, are you relying on the getInstance method?
> The WireModule uses the
> Guice SPI to analyze declared bindings for missing dependencies, so
> if you use the getInstance method programatically then this won't be
> observed by the WireModule and it won't add a binding that instead
> delegates to the BeanLocator.


Yes. Maybe I am missing something very trivial, but what's the correct way of creating an instance or injecting members of an instantiated object using Sisu?

When bootstrapping you can either use the BeanLocator to create the root components, or add @EagerSingleton to their types which creates them as soon as the injector is created. Non-root components should then use JSR330 and avoid needing to know about the BeanLocator or Injector.

> Alternatively you could always use the BeanLocator to
> programatically query and instantiate objects:

>
>     beanLocator.locate( Key.get( myType ) ).iterator().next().getValue()
>

> This could be hidden behind a utility method to limit dependency of
> your code to Sisu-specific types.


No problem to depend on Sisu, but I just can't manage to have the above example work. I annotated the implementation with Named and also bound it using a declared module (although I believe only one of the two should be needed) but object creation still fails, complaining that no implementation can be found for the interface.

It sounds like either you have multiple locators (effectively a split-brain scenario) or for some reason it is not analyzing your implementation.  You can always use -Dsisu.debug to turn on internal tracing of bindings, etc.

> BTW, is your code available somewhere?  I'm always interested in how
> people use and extend Sisu :)


I am sorry I cannot share the application code, it's closed source.
In any case so far I am just prototyping, but the idea is to include Sisu in the application. Of course I like DI, and also Guice AOP. Sisu seems like a Guice on steroids, with a higher level API. Sisu provides more types of wiring and it should allow writing an application that, with limited changes, can be adapted to run both on OSGi and non-OSGi containers. As an Eclipse plug-in developer, I think Sisu has great potential for supporting most of the use cases that in an Eclipse 3.x RCP are normally implemented using either the Equinox Registry or the OSGi Service Registry, but Sisu would provide a unified programming model and at the same time a simpler API for the developer. In short, it looks like a big win.

That's the goal - write re-usable components using JSR330 and let Sisu manage the wiring.

BTW, here's a simple Swing example that works both in classic and OSGi modes:


I'm also working on a new OSGi tutorial which is a bit more "real-world"

GianMaria.
_______________________________________________
sisu-users mailing list
sisu-users@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/sisu-users


Back to the top