I agree that the need for instantiating injected classes in generators
comes up all the time. I ran into it twice already, your use case is
another example, and you can also look at the code of GUIT who have
built their own GIN extension system:
http://code.google.com/p/guit/source/browse/trunk/Guit/src/com/guit/rebind/gin/GinInjectorGenerator.java
[Incidentally, you may want to look at this for a nice alternative to
the external tool generator you're currently using.]
I think your proposal is a good idea (although I'm not sure I entirely
understand it 100%, sample code for SwitchableViewExtension would
help). I think something like that would lead to an even cleaner
mechanism for injecting generated classes. However, I think it is more
involved and will probably require more convincing to get into GIN
proper. ;)
For this reason I suggest we take your proposal over to another thread
and keep this one focused on my original idea of allowing for an
annotation-parameterized get() in the ginjector. I think my proposal
goes further than allowing injected generators, as it emulates one of
the missing feature of GIN:
- Guice lets you retrieve a provider or an instance from the injector
given or a Class<?> or a Key.
- GIN lets you retrieve a provider or an instance from the injector
given a Class<?>.
What I am proposing here is a simple mechanism to extend GIN's
functionality to match that of Guice while keeping GIN-specific design
constraints in mind. Use cases for this feature go beyond simplifying
injected generators and include any situation in which you would use
Guice's injector.getInstance(Key) or injector.getProvider(Key).
Cheers,
Philippe
> --
> You received this message because you are subscribed to the Google Groups "google-gin" group.
> To post to this group, send email to googl...@googlegroups.com.
> To unsubscribe from this group, send email to google-gin+...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/google-gin?hl=en.
>
>
Once you have an instance of the ginjector you can use reflection to
find the correct method to call within it to instantiate any of your
dependencies. The problem, now, is that the ginjector needs to contain
a method returning your desired type. GUIT (again, IIUC) solves this
by defining their own custom Ginjector, wrapping the standard
Ginjector and automatically adding methods to it.
This strikes me as a sound idea to tackle your problem, but to be
really useful it would need to be done in a more generic way. One idea
-- and maybe that's what you were proposing -- might be for the
generator to somehow publish all the dependencies it needs. GIN could
then collect all these extra dependencies, add methods to the
ginjector somehow, and report back the name of these methods to the
generator so it can use them in the generated code. I think all of
this can be done entirely outside of GIN -- although it would probably
easier to do wighin GIN. I would love to work with you on this. [*]
An alternative (and that's what I'm aiming at with my proposal) would
be to have the ginjector support some kind of parameterized method.
There you go, my original proposal has completely derailed... :) I
still want to do it, however,as I'm already working on it, although
outside of GIN.
Cheers,
Philippe
[*] Even better than this would be for the generator to not care about
publishing its dependencies and to simply generate code containing
@Inject wherever needed. This looks very hard to do, however, as it
would require us to first generate the code, then load the class
(using some custom class loader?) to parse the AST and find the
dependencies, then bind the generated class in GIN... Anyway,
half-baked thought but looks hard.
Yes!
If Guice used some abstraction instead of direct java.reflect.* class,
it would be possible to plug the com.google.gwt.core.ext.typeinfo.* as
the source, but it doesn't AFAICT and I don't see any reason it would
change.
Using a custom ClassLoader could be an option too, and reconstructing
a Class from a JClassType maybe isn't that hard (using something like
Javassist for instance); I'll have to investigate...
Note the generator would still have to generate a binding from the
interface/class to the generated implementation, then I suppose Guice
would get it from the ClassLoader and find the dependencies itself (no
need to "parse the AST and find the dependencies, then bind the
generated class in GIN").
If possible (and there's no reason it could given what DevMode is
capable), could be both easier for the "GIN extension" writer *and*
even to implement the extension mechanism in GIN itself!
Ok, I think I'm beginning to grasp your idea, and I like it.
Also, do we need to use annotations? Maybe we could call something like:
bind(Foo.class).toGinerator(FooGinerator.class);
in the module? Since the module is evaluated at code-generation time
anyway... This might require some more hooks into Guice as it's the
one interpreting the module, and I believe it's currently not that
easy to hijack its DSL.
[Ginerator, such a great name. ;)]
>> GIN could
>> then collect all these extra dependencies, add methods to the
>> ginjector somehow, and report back the name of these methods to the
>> generator so it can use them in the generated code.
>
> That's not necessary: the generated class is supposed to be
> instantiated by GIN (and because Guice/GIN cannot analyze the
> generated class for dependencies, the generator gives them to GIN, as
> bindings to, say, "inject class X annotated with Y as first
> constructor argument") so the injection of the dependencies is done by
> GIN as with any other class, not need to communicate anything back to
> the generator (unless I'm missing something).
Ok, let me try to rehash to see if I understand your proposal:
- Gin detects a binding from Foo to a FooGinerator in the module
- Gin calls FooGinerator.collectDependencies() which returns, say, a
dependency on Bar.
- Gin calls FooGinerator.generateClass() which generates the class and
returns "com.google.client.FooImpl".
- Gin creates the following method in the ginjector:
Provider<Foo> getFooProvider() {
return new com.google.client.FooImpl( getBarProvider().get() );
}
Something like that?
One question: how easy is it to generate a class outside the standard
GWT <generate-with> path? Do we have to worry about cascading
generation? (i.e. FooImpl calling GWT.create())
>> I think all of
>> this can be done entirely outside of GIN -- although it would probably
>> easier to do wighin GIN. I would love to work with you on this. [*]
> [...]
>> [*] Even better than this would be for the generator to not care about
>> publishing its dependencies and to simply generate code containing
>> @Inject wherever needed. This looks very hard to do, however, as it
>> would require us to first generate the code, then load the class
>> (using some custom class loader?) to parse the AST and find the
>> dependencies, then bind the generated class in GIN... Anyway,
>> half-baked thought but looks hard.
>
> Yes!
Yes to: looks hard, or to the fact that it would be great?
> If Guice used some abstraction instead of direct java.reflect.* class,
> it would be possible to plug the com.google.gwt.core.ext.typeinfo.* as
> the source, but it doesn't AFAICT and I don't see any reason it would
> change.
> Using a custom ClassLoader could be an option too, and reconstructing
> a Class from a JClassType maybe isn't that hard (using something like
> Javassist for instance); I'll have to investigate...
Not sure why you would need to recreate a JClassType from a java type
(the generator gives you back to fully qualified class name, no?), but
in case you can, Gin's KeyUtil has a bunch of methods for that:
http://code.google.com/p/google-gin/source/browse/trunk/src/com/google/gwt/inject/rebind/util/KeyUtil.java?spec=svn130&r=61#132
> Note the generator would still have to generate a binding from the
> interface/class to the generated implementation, then I suppose Guice
> would get it from the ClassLoader and find the dependencies itself (no
> need to "parse the AST and find the dependencies, then bind the
> generated class in GIN").
> If possible (and there's no reason it could given what DevMode is
> capable), could be both easier for the "GIN extension" writer *and*
> even to implement the extension mechanism in GIN itself!
The way I see it, the module provides the binding. The goal of the
class loader is simply to make the generated code accessible via
reflection to identify the dependencies and injection points
automatically.
> I unfortunately haven't had the time to fully explore this and especially
> need to talk to the GWT team. But essentially your musings above are very
> similar to what I'd like to do:
>
> Expand the GWT generator framework to allow me to call GWT.create
> programatically and inspect the results (i.e. load them into the AST).
A first step would be to skip all the deferred binding performed by
GWT.create and to invoke directly a generator specified in the module.
> Expose the freshly generated/loaded classes through a custom class loader
> (here we should be able to leverage the dev mode infrastructure which does
> something similar).
Just looked quickly through GWT code and I wonder if we could use
their own class loader. Take a look there:
http://code.google.com/p/google-web-toolkit/source/browse/trunk/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java#646
And there:
http://code.google.com/p/google-web-toolkit/source/browse/trunk/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
> Us the above custom classloader for Guice (presumably iteratively since some
> of the targets are only identified and generated during the binding process)
> and inspect the resulting bindings that way.
Again, assuming we do not try to mix deferred binding and gin
injection, I think we could all the classes to generate are exposed in
the module, so no need for iterative generation.
> These changes allow us to do several great things:
>
> If we can call GWT.create programatically, for the first time we can
> actually test generators properly!
> No more "Deferred binding result type 'A' should not be abstract" errors -
> we can actually check if GWT.create bindings work during GIN compilation.
> No more requirements that the java sources need to be compiled before the
> GWT compiler runs (we can just always use the custom class loader).
>
> And of course we can finally have generated code use Gin - and vice versa.
> All of this will take time though, since we probably have to make changes to
> both Guice and GWT and my bandwidth is limited. I can however provide more
> direct access to the people running those projects so that should help get
> reviews and such. :) In the meantime, maybe extensions are an interesting
> way to go (they might prove useful no matter what anyhow) and of course we
> should not forget that there are additional features that we need: enabling
> default-access injections and child-injectors/private modules are good
> examples which we should at least have an idea about so we don't build
> something that doesn't work with those things.
> What do you think? :)
I would love to bounce these ideas with people more closely involved
in GIN and GWT. I agree with you we should take things step by step.
In order of difficulty I see:
1) Emulation for get(Key) in the ginjector via Foo get(Annotation);
2) Support for extensions (Thomas proposal)
3) Reflective inspection of generated classes to identify dependencies
and injection points
What do you think?
Philippe
The thing is that the GinModule is client-code (both used in Java at
compile-time and translated to JavaScript for the @Provides methods
and inner classes, providers for instance; the configure() method
won't be called in the generated code, so it'll be pruned by the
compiler, but it still has to be "translatable"), this means the
MyGinExtension has to be translatable too. You don't have that issue
with classes referenced from annotations (see @Service and @ProxyFor
in RequestFactory for instance).
I'm not sure what it'd buy you really (see below) but it could only be
possible if each and every extension provides a <super-source> no-op
implementation; easy but still cumbersome. Now let's see the
differences with my annotation-based approach: if your module
referenced in @GinExtensions can tell the GinGenerator that it handles
bindings to specific types (i.e. whichever their parameter types, for
generic types, or maybe even their specific type, if reacting on
annotations) I think there's no need for a bind().toExtension(). The
benefit of your proposal is that no 2 extensions can conflict because
every binding has to be explicit; the drawback is that... every
binding has to be explicit!
Imagine 30 screens where I would need a SwitchableView (I haven't
checked but I suspect it'll be in this order of magnitude, probably
even more), it means 30 bind().toExtension() in my GinModule! On the
other hand, I'm proposing only adding a
@GinExtensions(SwitchableViewExtension.class) on the GinModule to
handle all subclasses of SwichableView; and that could be used to
refactor into extensions the AssistedInject, AsyncProvider and
RemoteService special-casings in GIN.
> 2) Should we first go with a GinGenerator.getDependencies() method
> (and some setDependencies() on the code generated by the
> GinGenerator?) or do we only expect a GWT Generator and analyse the
> generated code for @Inject dependencies and injection points?
Well, GIN's com.google.gwt.inject.rebind.binding.Binding has
getRequiredKeys and writeCreatorMethod, and all existing "extensions"
have specific bindings (AsyncProviderBinding, FactoryBinding and
RemoteServiceProxyBinding), so maybe it's the way to go; enhancing the
API with a way to create any file you want (so you can reference the
generated class from the writeCreatorMethod). I think a
CompilingClassLoader could later be plugged behind that API (Guice's
com.google.inject.spi.InjectionPoint makes it easy to analyze a class
and grab its dependencies, which makes it "easy" to build the
RequiredKeys and generate the injection code for writeCreatorMethod).
> Looking forward to working on this with you!
What do you think would be the best way to collaborate? Fork on
github? (I'm not used to DVCS but it could be the perfect excuse to
start using it) Exchange patches on codereview.appspot.com? (so each
one is free to work with its SVN working copy and/or local branches in
Git (or Mercurial)) Or maybe "collaboratively" spec it all in Wave or
Docs before coding it?
--
Thomas Broyer
/tɔ.ma.bʁwa.je/
Gotcha! (Finally...) I also appreciate your desire to bind a bunch of
interfaces at once to the same generator. This is specific to the fact
that we are binding to "generators" which may inspect the methods
provided in the interface to yield various implementations. To me, it
calls for a binding of this type:
bindAll(SwitchableView.class).to(GinGenerator.class); [*]
But this brings us back to the problem that GinGenerator must be
client code... So, let's go with a @GinExtension annotation.
Still, I think it's a bit weird that some of the bindings are done in
modules and some in the extensions.
However, maybe these GinExtension could be expected to have a
configure() method with bindings such as the above? And maybe to
ability to install() some GinModules? This way a single gin extension
could configure all the bindings it needs, including bindings to
GinGenerators, but it wouldn't be expected to run as client code? In
other words, GinExtensions would just be GinModules with slightly less
privileges.
>> 2) Should we first go with a GinGenerator.getDependencies() method
>> (and some setDependencies() on the code generated by the
>> GinGenerator?) or do we only expect a GWT Generator and analyse the
>> generated code for @Inject dependencies and injection points?
>
> Well, GIN's com.google.gwt.inject.rebind.binding.Binding has
> getRequiredKeys and writeCreatorMethod, and all existing "extensions"
> have specific bindings (AsyncProviderBinding, FactoryBinding and
> RemoteServiceProxyBinding), so maybe it's the way to go; enhancing the
> API with a way to create any file you want (so you can reference the
> generated class from the writeCreatorMethod). I think a
> CompilingClassLoader could later be plugged behind that API (Guice's
> com.google.inject.spi.InjectionPoint makes it easy to analyze a class
> and grab its dependencies, which makes it "easy" to build the
> RequiredKeys and generate the injection code for writeCreatorMethod).
>
>> Looking forward to working on this with you!
>
> What do you think would be the best way to collaborate? Fork on
> github? (I'm not used to DVCS but it could be the perfect excuse to
> start using it) Exchange patches on codereview.appspot.com? (so each
> one is free to work with its SVN working copy and/or local branches in
> Git (or Mercurial)) Or maybe "collaboratively" spec it all in Wave or
> Docs before coding it?
I propose a Wave doc for the design and a mercurial repo on Google
Code for the dev. (Or GitHub, but I'm more fluent in mercurial.) Code
reviews on Rietveld. DVCS are really easy and fun to use, you have to
pick them up. ;)
Cheers,
Philippe
[*] In fact, a bindAll() method would be useful in Guice in some
situations. I have created something like that in Jukito where
forceMock(Foo.class) binds all classes assignable to Foo to a
MockProvider<>.
It's not really different to GWT generators, and to the existing AsyncProvider.
> However, maybe these GinExtension could be expected to have a
> configure() method with bindings such as the above? And maybe to
> ability to install() some GinModules? This way a single gin extension
> could configure all the bindings it needs, including bindings to
> GinGenerators, but it wouldn't be expected to run as client code? In
> other words, GinExtensions would just be GinModules with slightly less
> privileges.
Any concrete use-case?
How about rather having to use the extension as an install(someModule)
or @GinModules(SomeModule.class), with the installed module being
annotated with @GinExtension (and/or extending a special-cased
GinModule).
Servlet integration in Guice is done that way for instance: if you
want RequestScope and SessionScope (but not the specific DSL) you just
install(new ServletModule()). Refactoring AssistedInject as a GIN
extension could work that way too: you use FactoryModuleBuilder
explicitly, so the installed module could be annotated with
@GinExtension (instead of having to use both FactoryModuleBuilder and
@GinExtension to use the AssistedInject extension).
> I propose a Wave doc for the design
I've created a world-readable, group-writeable Wave.
https://wave.google.com/wave/waveref/googlewave.com/w+BcDOfmlPA
For the moment, please do only add comments in separate blips and
leave the main document editing to Philippe, Peter and myself.
> and a mercurial repo on Google Code for the dev.
Or maybe Peter would let us use a branch on the GIN repo?
> (Or GitHub, but I'm more fluent in mercurial.)
GitHub has the advantage of "git svn", but otherwise I don't care at all.
--
Thomas Broyer
/tɔ.ma.bʁwa.je/
No, I think it's just a question of trying to match user's
expectancies as much as possible. Personally, if I some interface
bound to some implementation (generated or not), I expect to find a
corresponding binding in one of the modules. I could be convinced
otherwise.
I'm taking the rest of this discussion to the Wave.
Or maybe Peter would let us use a branch on the GIN repo?
Cheers,
Philippe
+1
(having it in 1.1 would be awesome !)
--
--
You received this message because you are subscribed to the Google Groups "google-gin" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-gin/-/KsRUkkv5dwsJ.
Hi Stefano,unfortunately this got delayed a lot and isn't ready yet since the work on private modules introduced a lot of unanticipated problems that we're still dealing with.
Peter
On Wed, Jan 4, 2012 at 15:52, Stefano Ciccarelli <--> wrote:
Hello!What about this one? Is it in the trunk now?ByeStefanoTo view this discussion on the web visit https://groups.google.com/d/msg/google-gin/-/KsRUkkv5dwsJ.--
You received this message because you are subscribed to the Google Groups "google-gin" group.
To unsubscribe from this group, send email to google-gin+unsubscribe@googlegroups.com.
Hello all!
I regularly need to instantiate injected classes from my code produced
via GWT generators. For example, my recent patch to support gin-
injected widgets in UiBinder code requires this. [1]
For the moment, the only option I have found is for the generated
class to grab the global ginjector and use it to instantiate the
class. This, however, requires the user to add a method in the
ginjector returning the desired type. This extra burden of adding a
method there is very tedious and does not keep change local.
Now, this would be solved if GIN supported Guice-like accessors in the
ginjector, as proposed in issue 89 [2]. However, as discussed by
aragos in this issue, it is not desirable in GIN because it would
force-compile a lot of classes that might not be needed. (All classes
if you take into account JIT binding and GWT.create instantiations.)
I have come up with a different proposal that would allow me to
elegantly solve the problem of instantiating injected classes in
generators without polluting the compiled code. What I propose is to
allow the ginjector to contain methods taking a single Annotation
parameter. For example:
Payment getPayment(Annotation annotation);
AsyncProvider<Payment> getPaymentAsyncProvider(Annotation
annotation);
Such methods would allow the ginjector to instantiate the Payment
object bound with the specified annotation. It could be used within
generators to instantiate a given object without having to add a
specific method to the ginjector. Moreover, only the implementations
of Payment bound with an annotation would be compiled-in. Other
classes and non-bound implementations of Payment would be left out.
That means having:
Object getObject(Annotation annotation);
would not force-compile the entire world, just classes bound with an
annotation. If we make the reasonable assumption that such classes are
desired (not an unreasonable assumption I believe) then it is not over-
compiling anything.
I'm currently working on an implementation of this feature outside of
GIN, wrapping the Ginjector within my new GinjectorWithAnnotations.
However, I would be happy to add this directly on the Ginjector class
if you thought you would be interested in adding such a feature to
GIN's trunk. Just let me know.
Cheers,
Philippe
[1] For more info on this, see: http://code.google.com/p/gwt-platform/issues/detail?id=74#c10
[2] http://code.google.com/p/google-gin/issues/detail?id=89
--
You received this message because you are subscribed to the Google Groups "google-gin" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-gin/-/leQzzSbN5CIJ.
To unsubscribe from this group, send email to google-gin+...@googlegroups.com.