Hmmm, my curiousity is piqued, what is this "Generator" you're referring to?
>From the sounds of it, there are posts which deal with getting cookies
from the browser. Some people have even posted up Java code to be run from your GWT client which wraps cookies for your retrieval. If you set the session in a cookie, your client side code could get it this way.
A non GWT specific method I've used in the past (for good or ill) is to put the session ID as a global javascript page variable. You could then grab that via JSNI to get the session ID.
Or maybe I've missed the point completely and your talking about something like the some sort of session created when the code is generated... though that sounds kinda silly.
Generator is the abstract class that allows you to generate classes on the fly (and compite them into JavaScript) using GWT.create.
Hence GWT.create(MyClass.class) will connect to the server, invoke Generator registered for this class, generate Java code, compile it into JavaScript, pass to the client, instantiate and return an instance to you. (Of course the point here is to generate some subclass that overrides some methods in MyClass). Or am I wrong?
Being lazy bastard I want to be able to send a snippet of code from server to client. This snippet might be different for different clients or different states.
Whoa... I just did some reading on generator. I initially thought you were trying to do something wacky and against the grain from GWT. I just deleted a huge post asking whether or not you were sure you knew what you were doing and there might be a better way. Then I did some research before posting (just to be sure) and realised that what you are doing is completely within the current trend of where GWT is moving to. I guess I just hadn't seen many posts on this issue.
So with my new found understanding of what you're trying to do, if you do want to use session information from your generator, I'd use the following algorithm:
1.) Server side call is made to the GWT servlet, but beforehand, you've used some sort of filtering process to get the session and put it in a thread local variable. 2.) The GWT specific calls are made which eventually find and invoke the generate call on your generator. 3.) Your generator grabs the session out of the thread local variable.
This is a perfect solution for something like a thread local variable, to hold the session after the request cycle has started. It's also a nice way to avoid having to pass the session as a variable on every method call.
Well, that's cute but who is to guarantee that the server-side call using RPC and the call to produce the class will use the same thread? Moreover, usually http servers employ thread-caching that means that user requests will be processed on random threads.
Or maybe tomcat assigns a thread per session? Doubt it.
I like this definition of ThreadLocal usage (http://crazybob.org/2006/07/hard-core-java-threadlocal.html): "Use a thread local variable (where each thread has a separate value) when you want to carry a value along without explicitly passing it from method to method down the call stack"
As long as the ThreadLocal variable gets the session variable overwritten at the start of the request - no matter what - then you are guaranteed that different request instances hitting different server threads will get access to the same session object. Have you used a ThreadLocal variable before? There are certain givens you have to allow for, like always ensuring you overwrite the variable at the start of the request, but I can guarantee you that this is a solid solution and has worked for me many times previously.
As long as you always intercept the start of the request and get the session into the ThreadLocal variable, the later call to your generate method will always get the same session for the given request.
Specifically, check out the following thread where someone tries to point out not to use ThreadLocal's for a thread-safe servlet and gets quite an accurate response as to why they are fine: http://www.jguru.com/faq/view.jsp?EID=150
> Generator is the abstract class that allows you to generate classes on > the fly (and compite them into JavaScript) using GWT.create.
> Hence GWT.create(MyClass.class) will connect to the server, invoke > Generator registered for this class, generate Java code, compile it > into JavaScript, pass to the client, instantiate and return an instance > to you. (Of course the point here is to generate some subclass that > overrides some methods in MyClass). Or am I wrong?
The Generator subsystem hasn't been well-documented yet, so you're a little on your own at the moment, though I think good examples can be found a few "rebind" packages in gwt-user.jar.
But I need to clear up a fundamental misunderstanding. There are three major GWT contexts under which code can run. There's client runtime, which is where all translated client-side code runs. There's server runtime, which is where service implementations run. (Any objects that are serialized back and forth will exist in both the client and server runtime.) Finally, there's *compile* time. This is code that runs only when the GWTCompiler is running (*1). Generators run ONLY at compile time. They never run in the server or client runtimes. The intuition you described above is useful, but incorrect. By the time your code is through compiling, all of the GWT.create() calls have been replaced with hard-coded new() operations.
This is why we generate multiple scripts as compiled output. We generate a script for every possible permutation of "rebind results", if you will. Most of the time, the only variability is in what browser is running. That's why you often end up with four to five scripts (IE, Mozilla (and maybe older Mozilla), Safari, and Opera) -- this is the result of GWTCompiler producing a script for each browser, which is implemented with GWT.create() calls in DOM. Which concrete browser class is selected depends on the results of a "user.agent" property. The value of this property is computed within your *.nocache.html file; that file in turn chooses the correct pre-compiled script that matches the results of the property evaluation. But the fundamental assumption is that all *possible* property result values have to be known ahead of time-- which means a finite and predetermined set of values.
It would be possible, in theory, to use a session ID inside of the nocache.html to map onto one of a predetermined set of property values, but the general case of using an arbitrary session ID (and having that value available to a Generator) is impossible.
On 8/18/06, I wrote: > Finally, there's *compile* time. This is code that runs only when the > GWTCompiler is running (*1).
*1) In addition to running under GWTCompiler, Generators also run during "hosted mode compile time", which occurs when your client-side Java code is being prepped to run under the hosted browser. The fact that everything seems to happen all at once in hosted mode makes it easy to misinterpret what's really going on. I didn't want to confuse the issue by mentioning this inline.
I wonder if this thread is a good place to ask a little about how we can use the generators ourselves. The package(s) ...gwt.core.ext.* are lightly documented and difficult to interpret correctly. I've fished around and suspected that they were part of the compilation cycle but I presume they are exposed as part of the API so that others can generate different target code.
Experimenting with these would lead to new insights, but it's not at all clear what the correct entry point is. The key method is clearly:
public abstract String generate(TreeLogger logger, GeneratorContext context, String typeName);
The typeName is the class name argument used in GWT.create() call, yes? The return value is the qualified class name to be substituted? The logger makes sense and the context clearly has more depth.
I presume the generated code is actually written in JavaScript via the GeneratorContext tryCreate method. Is that right?
The oracles are a way of accessing CompilationUnit elements at compilation time or for generating target code?
What I'd love to see, is a super simple 'hello world' example that adds a new generator to the compilation cycle. Maybe all it generates is a single JS function (that says hello?). I can see that this functionality was intended to be exposed or it wouldn't be in the API, but what conditions was it designed to address and what kind of extensions might I want to write with it?
PS: The TreeLogger is a hiearchical logger that prints either to the console or (in hosted mode) to the GWT console, right?
I realize that we want to focus on the GUI set and client/server model, but I would very much like to implement something that works like GWT's RCP with a more pluggable protocol model. I'd rather work along with the grain than against it. Any information that can put me on the road to fruitful exploration would be greatly appreciated.
> I wonder if this thread is a good place to ask a little about how we > can use the generators ourselves. The package(s) ...gwt.core.ext.* are > lightly documented and difficult to interpret correctly. I've fished > around and suspected that they were part of the compilation cycle but I > presume they are exposed as part of the API so that others can generate > different target code.
Yes, the ultimate intent is that people will be able to tie into our deferred binding system to do all sorts of cool things at compile time. The GWT team has a strong belief in, "Why put off until runtime what you can do at compile time?" We hope that ultimately people will find deferred binding to be as useful as (and more appropriate to the browser than) reflection.
However, this is really more of an advanced feature, and one we don't expect to see wide use of until the product as a whole is a little more mature. So we have not yet spent a lot of cycles on thoroughly documenting and supporting it.
> Experimenting with these would lead to new insights, but it's not at > all clear what the correct entry point is. The key method is clearly:
> The typeName is the class name argument used in GWT.create() call, yes? > The return value is the qualified class name to be substituted? > The logger makes sense and the context clearly has more depth.
Yes, yes, and yes. Actually, I believe the return value can also be a semicolon-delimited list of classes if you generate more than one.
> I presume the generated code is actually written in JavaScript via the > GeneratorContext tryCreate method. Is that right?
The generated code should actually be Java, also you're free to use JSNI in generated classes (RPC does this, in fact).
> The oracles are a way of accessing CompilationUnit elements at > compilation time or for generating target code?
TypeOracle is a lot like using reflection. It exposes a full model of the entire client code base that's available. PropertyOracle exposes the current value of all of the deferred binding properties (such as user.agent). Both of these can provide useful information to a Generator.
> What I'd love to see, is a super simple 'hello world' example that adds > a new generator to the compilation cycle. Maybe all it generates is a > single JS function (that says hello?). I can see that this > functionality was intended to be exposed or it wouldn't be in the API, > but what conditions was it designed to address and what kind of > extensions might I want to write with it?
Indeed, it was designed to be exposed so that people could do all the useful things in client code that are presently being done via reflection in Java server code. The possibilities are pretty limitless.
If you want to get sense of how it works, I would start with JUnitTestCaseStubGenerator, which is probably the simplest shipping Generator.
Get a JUnit test set up (junitCreator is useful here), and add the following JVM arg to the command line: -Dgwt.args="-gen gen". The gen flag tells the compiler to dump all generated files onto the file system. (The gwt.args system property lets you pass options to the shell in a JUnit run as if you were invoking GWTShell directly with those command line args). Map the newly created "gen" folder into your project, and find the generated subclass of your base test case. This class was produced by JUnitTestCaseStubGenerator, so you can use it as a reference when examining the code for the generator. You can even set a break point and step through it to watch it in action.
For more complicated examples, check out com.google.gwt.i18n.rebind or com.google.gwt.user.rebind.rpc. The RPC stuff really demonstrates the full power of what you can do.
> PS: The TreeLogger is a hiearchical logger that prints either to the > console or (in hosted mode) to the GWT console, right?
Exactly.
> I realize that we want to focus on the GUI set and client/server model, > but I would very much like to implement something that works like GWT's > RCP with a more pluggable protocol model. I'd rather work along with > the grain than against it. Any information that can put me on the road > to fruitful exploration would be greatly appreciated.
Hope this helps, I'll be happy to answer furthur questions. Scott
I was able to get the JUnit code generated (-gen option) and I've been looking at various examples I could find (BTW: The docs for JUnit module definition fails to mention the need to inherit com.google.gwt.junit.JUnit, which could cause someone less familiar with the toolkit no end of heartache).
I've come up with a Hello World-type proxy generator (which I hope to expand later). What I need to know is how to apply it. I will examine the RPC code next.
I've collapsed things as much as possible to create a viable test and, hopefull, something that can be used to explain this feature in simple terms. I just have no idea how I can tigger this now ;-), though my first thought it that there has to be a registry or factory that the GWT compiler relies on. I will begin my hunt for this in the RCP source...
I am starting to see how the type oracle can be used to inspect a class and generate suitable wrappers that do much more... very nice!
Scott Blum wrote: > > I wonder if this thread is a good place to ask a little about how we > > can use the generators ourselves. The package(s) ...gwt.core.ext.* are > > lightly documented and difficult to interpret correctly. I've fished > > around and suspected that they were part of the compilation cycle but I > > presume they are exposed as part of the API so that others can generate > > different target code.
> Yes, the ultimate intent is that people will be able to tie into our > deferred binding system to do all sorts of cool things at compile > time. The GWT team has a strong belief in, "Why put off until runtime > what you can do at compile time?" We hope that ultimately people will > find deferred binding to be as useful as (and more appropriate to the > browser than) reflection.
> However, this is really more of an advanced feature, and one we don't > expect to see wide use of until the product as a whole is a little > more mature. So we have not yet spent a lot of cycles on thoroughly > documenting and supporting it.
> > Experimenting with these would lead to new insights, but it's not at > > all clear what the correct entry point is. The key method is clearly:
> > The typeName is the class name argument used in GWT.create() call, yes? > > The return value is the qualified class name to be substituted? > > The logger makes sense and the context clearly has more depth.
> Yes, yes, and yes. Actually, I believe the return value can also be a > semicolon-delimited list of classes if you generate more than one.
> > I presume the generated code is actually written in JavaScript via the > > GeneratorContext tryCreate method. Is that right?
> The generated code should actually be Java, also you're free to use > JSNI in generated classes (RPC does this, in fact).
> > The oracles are a way of accessing CompilationUnit elements at > > compilation time or for generating target code?
> TypeOracle is a lot like using reflection. It exposes a full model of > the entire client code base that's available. PropertyOracle exposes > the current value of all of the deferred binding properties (such as > user.agent). Both of these can provide useful information to a > Generator.
> > What I'd love to see, is a super simple 'hello world' example that adds > > a new generator to the compilation cycle. Maybe all it generates is a > > single JS function (that says hello?). I can see that this > > functionality was intended to be exposed or it wouldn't be in the API, > > but what conditions was it designed to address and what kind of > > extensions might I want to write with it?
> Indeed, it was designed to be exposed so that people could do all the > useful things in client code that are presently being done via > reflection in Java server code. The possibilities are pretty > limitless.
> If you want to get sense of how it works, I would start with > JUnitTestCaseStubGenerator, which is probably the simplest shipping > Generator.
> Get a JUnit test set up (junitCreator is useful here), and add the > following JVM arg to the command line: -Dgwt.args="-gen gen". The gen > flag tells the compiler to dump all generated files onto the file > system. (The gwt.args system property lets you pass options to the > shell in a JUnit run as if you were invoking GWTShell directly with > those command line args). Map the newly created "gen" folder into > your project, and find the generated subclass of your base test case. > This class was produced by JUnitTestCaseStubGenerator, so you can use > it as a reference when examining the code for the generator. You can > even set a break point and step through it to watch it in action.
> For more complicated examples, check out com.google.gwt.i18n.rebind or > com.google.gwt.user.rebind.rpc. The RPC stuff really demonstrates the > full power of what you can do.
> > PS: The TreeLogger is a hiearchical logger that prints either to the > > console or (in hosted mode) to the GWT console, right?
> Exactly.
> > I realize that we want to focus on the GUI set and client/server model, > > but I would very much like to implement something that works like GWT's > > RCP with a more pluggable protocol model. I'd rather work along with > > the grain than against it. Any information that can put me on the road > > to fruitful exploration would be greatly appreciated.
> Hope this helps, I'll be happy to answer furthur questions. > Scott
On 8/19/06, claude <claude.e.dug...@gmail.com> wrote:
> I was able to get the JUnit code generated (-gen option) and I've been > looking at various examples I could find (BTW: The docs for JUnit > module definition fails to mention the need to inherit > com.google.gwt.junit.JUnit, which could cause someone less familiar > with the toolkit no end of heartache).
Actually, we removed this requirement in 1.1.0 to make things simpler. You no longer have to inherit from the JUnit module; JUnitShell makes you inherit it automatically when you're running a JUnit test. So we're kind of "cheating" here. :)
> I've collapsed things as much as possible to create a viable test and, > hopefull, something that can be used to explain this feature in simple > terms. I just have no idea how I can tigger this now ;-), though my > first thought it that there has to be a registry or factory that the > GWT compiler relies on.
To kick things off, you have to setup a rebind rule. This is a rule, somewhere in your module definition (or inherited modules) that tells GWT to look for a specific sort of type, and invoke your Generator if that type is found. Usually the trigger is that the requested type is a subclass or implementor of some marker type. Take a look at JUnit.gwt.xml and RemoteService.gwt.xml for examples.
I just figured this out and was able to get a generator working. Three quick caveats, should anybody try to use the code I posted.
1) The posted code needs to call writer.commit(logger) just before returning from the generate class.
2) There are syntax errors in my generated code that needs to be corrected, a missing end quote and semicolon.
3) There are apparently multiple calls made into this code, durring which the print writer may be null, so defensive programming is called for. It appears that bypassing write calls when the writer is null, does not cause an overall failure, and the output is correctly written to file (use -gen), so I am presuming that multiple calls are made. In any case, null writers need to be accounted for and writing skipped in those cases.
Also - You must call GWT.create(SourceClassName.class); before the compiler is invoked and the class must implement (or I presume extend) the interface (or class?) declared in the 'when-type-assignable' XML tag.
All the fixes are trivial and documented in this thread, so I probably won't re-post the corrected class .
Item 3 may be a bug. It would be good to know if this code nees to be re-entrant and whether null writers are expected, or whether I'm doing something wrong and could fix it.
I'm very impressed, as I almost always am by Google code and products. I can totally buy into the compile it in advance if you can and I can see that in most cases reflection is not important (though I'm certain that some cases exist, they probably very rare).
And now, I can try to create something useful with this new-found magic incantation ;-).
Our messages must have crossed paths. The next thing I need to learn are the oracles, I'll probably build something simple first, but I may have more questions. I'll post them under a new thread.
On 8/19/06, claude <claude.e.dug...@gmail.com> wrote:
> 3) There are apparently multiple calls made into this code, durring > which the print writer may be null, so defensive programming is called > for. It appears that bypassing write calls when the writer is null, > does not cause an overall failure, and the output is correctly written > to file (use -gen), so I am presuming that multiple calls are made. In > any case, null writers need to be accounted for and writing skipped in > those cases. > Item 3 may be a bug. It would be good to know if this code nees to be > re-entrant and whether null writers are expected, or whether I'm doing > something wrong and could fix it.
This is the intended design. When your Generator tries to create a class that already exists in the current compile, trying to get a source writer returns null to let you know the class already exists. The code that calls your Generator has no idea what classes you're going to generate, so it calls your Generator even if you don't need to generate anything.