> 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).
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
|