Custom Generators

97 views
Skip to first unread message

claude

unread,
Aug 20, 2006, 6:36:49 PM8/20/06
to Google Web Toolkit
After a little more experimenting with a custom Generator
implementations this morning, I have a few observations and questions:

1) Major gotcha that took a while to track down: if you return null in
the generator, there is no failure. The target object is created (and
can be found in the gen directory if -get is used) but the GWT.create
fails silently. Not sure if this is intentional, but it's tricky to
track down.

A little background: My experimentation project is an XML import/export
feature that generates a subclass of a bean object implementing the
IRemotableBean interface. For each such object, three methods are
currently generated:

public Element buildElement(Document doc);
static <SourceObject> parseElement(Element element);
String toString();

The buildElement method returns an XML Element object, populated from
the bean properties, possibly recursively. The parseElement static
method, returns an instance of the source object (which is actually a
GWT.create proxy) and toString creates an XML Document that anchors the
first child to the Element acquired using a call to buildElement in the
same class. The net effect is intended to be that you can create and
parse XML to and from objects that implement the IRemotableBean
interface.

NOTE: For anybody who needs to ask why XML is important, or why not use
GWT RPC, the answer is interoperability. 'nuff said!

Now the problem/question:

2) When I run the generator on a single class that holds no references
to other IRemotableBean objects, there is no problem. I can build and
parse all primitve object properties and basic object wrappers at the
moment. Generated methods use proxied objects to call other
IRemotableBean objects recursively if necessary, delegating to static
parseElement calls as may be required.

My generated bean references cause "cannot be resolved to a type"
errors. From all appearances, the gen directory does not contain the
referenced objects. The implication is that the compiler is being
called before the generator has a chance to create all the proxy
classes.

Scott (you) mentioned that multiple object names can be returned by the
generate method. Is this the way to bootstrap the generation of other
classes before the compilation phase? What's the
analyze/generate/compiler sequence supposed to be?

Thanks, as always...

Scott Blum

unread,
Aug 21, 2006, 6:05:58 AM8/21/06
to Google-We...@googlegroups.com
Hi claude,

I can't say I completely followed what you're doing exactly, but I'll
try to answer the question.

> My generated bean references cause "cannot be resolved to a type"
> errors. From all appearances, the gen directory does not contain the
> referenced objects. The implication is that the compiler is being
> called before the generator has a chance to create all the proxy
> classes.

I'm having a little trouble fully understanding this part, but it
seems from your description that one of three things might be
happening.

1) Your generated classes are directly referencing some other
"generated" classes which do not exists. If this is what's happening,
you can't expect your Generator to ever be called more than once, so
you need to generate all the classes you'll need in one shot and
return the list of generated classes as a semicolon-delimited string.

2) Your generated classes are trying to GWT.create(), but are making
direct references to non-existent types. This is illegal; correct use
of GWT.create() means you only ever directly reference existing types.
Your generated class has to be compilable before rebinds are
resolved; we have to parse the class in order to find the rebind
calls.

3) Your generated classes contain GWT.create() calls; you aren't doing
anything illegal; but for some reason the generated GWT.create() calls
are not being handled recursively. If this is the case, it's possible
you've stumbled onto a bug in the rebind system. We may not have
specifically tested this case.

If none of these apply, I'd need more detail to help you furthur.

> Scott (you) mentioned that multiple object names can be returned by the
> generate method. Is this the way to bootstrap the generation of other
> classes before the compilation phase? What's the
> analyze/generate/compiler sequence supposed to be?

Yes, returning multiple classes from your Generator is a very common
way of returning a whole family of related classes to implement a
single rebind. RPC does exactly this; a single remote service rebind
creates a Proxy class, a TypeSerializer, and many FieldSerializers.

The analyze/generate/compile sequence is not well-specified. In
general, you can never directly refer to any generated class; the only
exception being that classes returned together for a single Generator
invocation can directly refer to each other.

HTH,
Scott

claude

unread,
Aug 21, 2006, 2:34:30 PM8/21/06
to Google Web Toolkit
I was expecting each implementation of the triggering interface to call
the generator for processing before compilation woudl eb attempted,
more like the Eclipse Builder process, so I have to rethink this. It
seesm counter-intuitive to use the TypeOracle to reach out for other
classes that implement the same interface in a batch processing call,
since the rules relate to each implementation of a given interface.

The RPC implementation uses an entry point interface RemoteService and
then traverses other classes (that it references?) that implement
IsSerializable, I presume. I can take a similar route using an
IRemotableDAO that looks for IRemotableBean classes. A couple of
questiosn come to mind:

1) Why not do a separate generation -> compilation pass so that missing
types are not an issue until the compilation phase?

2) The generator API implies a particular type (class) is being
processed on each call. It's not clear what batch processing should be
required here. What if the same class is referenced by two generator
triggering interface implementors (causing two generator calls that
need to build the same dependency)?

3) What happens in cases where recursive references are required? If I
need to process say a tree Node implementation that references other
instances of the same type, do I have to hunt them all down on the
first generator call.

4) What do I return from the generator method? To use the recursive
tree structure example, say the first generator call causes a Document
node to be handled but this node references an Element node that also
implements the Node interface (not related to your XML implementation
but a common Tree-type example). If I need to process all references
recursively on the generator call stimulated by the first match, what
do I do I return? A few options are:

a) Return the list of all processed classes on that call. This seems
obvious but how can I do that when the SourceWriter is null. Since I've
see that GWT.create is finiky about this being returned, I'm not clear
how this would work.

b) Return the main and all referenced classes that were processed even
if they were already generated. I think this is ok.

3) The key puzzle to me is that the design assumes that an entry point
class and a group of related classes are processed as a set. What's
leass clear is how classes that overlap should be handled. That is to
say, classes used in two entry points. For complex cases that reuse
generated classes, this is not so obvious.

4) Finally, I think (your) explanations 1and 3 are applicable in my
case. To simplify, I am generating Bean1 and Bean2. If Bean1 references
Bean2, because I want to use generated methods, I must do a cast in the
form:

Bean2Proxy proxy = (Bean2Proxy)GWT.create(Bean2.class);
proxy.callgeneratedMethod();

If the class being generated (Bean1) makes such a reference, I get the
missing type error, because the Bean2 class has not been processed yet.
It might be to build a simple test case that verifies this.

claude

unread,
Aug 22, 2006, 11:20:52 AM8/22/06
to Google Web Toolkit
FYI,

I worked on this a little last night and realized (as I was tracing
through dependencies) that I had encapsulated the GWT.create calls in
the class that was being proxied rather than in any class that depended
on it. This explains why the recursive dependencies were not found by
the compiler. I'll be looking into this later today, but I suspect this
is the most likely cause of the errors I saw. I'll let you know what I
find.

Scott Blum

unread,
Aug 22, 2006, 12:26:35 PM8/22/06
to Google-We...@googlegroups.com
Glad to hear that. :)

I was just about to reply to your previous post to say: It seems like
you're now at the point where you're hitting up against the fact that
the Generator subsystem isn't yet documented or fully supported. Feel
free to continue to ask questions, but try to make them specific and
relatively succinct; I think you're mostly going to make progress by
experimentation from here. You've got a lot going on now, really more
context than I can afford to take the time to fully understand, and to
be helpful, I would probably be doing the same sort of experimentation
you're already doing.

Let me know how it goes!

Scott

claude

unread,
Aug 22, 2006, 4:12:32 PM8/22/06
to Google Web Toolkit
I agree. Some of my explanations sounded somehwat obscure even to me
when I read them back. It's hard to convey that kind of context in a
few paragraphs. This brings to mind an important question:

Are the member of the GWT development tool planning to write more
documentation and/or publish some articles. I've written quite a bit
over the years and I've though of writing about GWT more than once
(it's been a while - the last thing I wrote was about Extending JMX
support in Spring for DeveloperWorks -
http://www-128.ibm.com/developerworks/java/library/j-springjmx almost a
year ago).

The community desperately needs something deeper than another 'how to
get up and running, and isn' this cool' article.

BTW: I've been looking a the RPC code quite a bit, and it's actually
starting to make sense. I'll ask if I get into a corner after trying
what I planned to try tonight. Thanks for being receptive to my
questions. Did you work on the source code Scott? Are you playing both
a development and support role?

claude

unread,
Aug 22, 2006, 9:20:35 PM8/22/06
to Google Web Toolkit
OK, here's some code I'm generating, which I hope is enough to get the
drift of what I'm doing across.

I've tried generating all matching types at once, but this is the first
one processed, and it fails anyway, so I reverted to letting the
generator do one at a time. If I reference only do the two primitive
container objects (which contain all the primitive types (boolean,
byte, short, int, long, float and double) and the primitive objects
(which are the standard object wrappers (Boolean, Byte, Short, Integer,
Long, Float and Double) in my entry point class, they generate/compile
just fine. When I introduce a source class that references the
generated types, I get errors.

Presuming that casting to a proxy was an issue, I separated the calls
into multiple lines. So here you can see that the generated
CompoundObject (which does nothing but contain the
PrimitiveObjectContainer and PrimitiveTypeContainer), calls GWT.create.

This process does not apparently cause a recursive call to build the
proxies when the create call is encountered in the generated class
since I get a "cannot resolve to a type" error on the proxy class
references which occur in subsequent lines, respectively. The casting
to generated type is necessary to access the generated method.

This is stripped down code (ie: I commented out the generation of other
methods for testing purposes but they have no impact on the test). Any
insights would be greatly appreciated. It looks to me like GWT does not
recursively generate classes when it sees the create method in another
generated class. Do you need me to dig deeper to confirm this?

The GWT's RPC code (which is my only model) is fairly indirect about
what it does (functional but not as elegant as it could be). I think it
tries to determine in advance what the dependency tree is. Is that
true? Is it necessary to do a full analysis in advance to determine the
build order for all the proxies? If that's the case, the design is
limited to acyclic graph structures. Is that the case? My example isn't
cyclic yet but I expect this solution to handle cyclic references once
I'm finished, so it might be good to understand such a limitation in
advance and consider a different aproach.

Do you think doing the dependency analysis in my code and generating
classes in the right order will overcome this issue? I was hoping not
to have to do that...

Here's the generated code:

package com.claudeduguay.gwt.generator.client.test;

import com.google.gwt.core.client.*;
import com.claudeduguay.gwt.generator.client.*;
import com.google.gwt.xml.client.*;
import com.claudeduguay.gwt.generator.client.value.*;

public class CompoundObjectRemotableBean
extends com.claudeduguay.gwt.generator.client.test.CompoundObject
{
public void parseElement(Element element)
{
// Attributes
// Children
NodeList children = element.getChildNodes();
int count = children.getLength();
for (int i = 0; i < count; i++)
{
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE)
{
Element child = (Element)node;
String tagName = child.getNodeName();
if (tagName.equals("PrimitiveObjectContainer"))
{
PrimitiveObjectContainer primitiveObjects =

(PrimitiveObjectContainer)GWT.create(PrimitiveObjectContainer.class);
PrimitiveObjectContainerRemotableBean primitiveObjectsProxy =
(PrimitiveObjectContainerRemotableBean)primitiveObjects;
primitiveObjectsProxy.parseElement(child);
this.setPrimitiveObjects(primitiveObjectsProxy);
}
if (tagName.equals("PrimitiveTypeContainer"))
{
PrimitiveTypeContainer primitiveTypes =

(PrimitiveTypeContainer)GWT.create(PrimitiveTypeContainer.class);
PrimitiveTypeContainerRemotableBean primitiveTypesProxy =
(PrimitiveTypeContainerRemotableBean)primitiveTypes;
primitiveTypesProxy.parseElement(child);
this.setPrimitiveTypes(primitiveTypesProxy);
}
}
}
}
}

claude

unread,
Aug 22, 2006, 9:22:16 PM8/22/06
to Google Web Toolkit

Scott Blum

unread,
Aug 23, 2006, 12:27:00 AM8/23/06
to Google-We...@googlegroups.com
On 8/22/06, claude <claude....@gmail.com> wrote:
> Are the member of the GWT development tool planning to write more
> documentation and/or publish some articles. I've written quite a bit
> over the years and I've though of writing about GWT more than once
> (it's been a while - the last thing I wrote was about Extending JMX
> support in Spring for DeveloperWorks -
> http://www-128.ibm.com/developerworks/java/library/j-springjmx almost a
> year ago).

I think we will at some point, but I wouldn't let that stop you from
writing stuff. :)

> BTW: I've been looking a the RPC code quite a bit, and it's actually
> starting to make sense. I'll ask if I get into a corner after trying
> what I planned to try tonight. Thanks for being receptive to my
> questions. Did you work on the source code Scott? Are you playing both
> a development and support role?

Yep. I mainly work on the compiler and hosted mode integration,
though recently I've also done a lot with RPC and JUnit, and a little
with the UI framework. Come to think of it, I've worked on almost
every piece of GWT. :D Which is why (especially with the upcoming
release) I can only devote a small amount of time to answering your
questions on this topic. I'm about to follow up to your next email
with a couple of points, but I can't do anything like dig into your
code anytime soon.

Scott

Scott Blum

unread,
Aug 23, 2006, 12:36:02 AM8/23/06
to Google-We...@googlegroups.com
On 8/22/06, claude <claude....@gmail.com> wrote:
> I've tried generating all matching types at once, but this is the first
> one processed, and it fails anyway, so I reverted to letting the
> generator do one at a time.

I don't know what you mean by "all matching types". For the record,
you can only ever rebind one input class to one output class for the
entire scope of one compilation. That means if your generator gets
com.A as input and spits out com.B, com.A will ALWAYS resolve to com.B
for the rest of the compile, and multiple GWT.create(com.A) will not
cause your generator to run again. Does this break an assumption you
were making?


> When I introduce a source class that references the generated types, I get errors.

To reiterate something I said before, you can never, ever directly
refer a generated class name in source code. Except in the *one*
special case where you generate a SET of files from one single
Generator.generate() call. The set of files you generate from a
single Generator.generate() call MUST be able to compile (e.g. javac)
with only themselves and any non-generated source that may be
available. It is only after the set of output files has been compiled
without error that furthur rebinds can take place.

HTH,
Scott

claude

unread,
Aug 23, 2006, 10:44:32 AM8/23/06
to Google Web Toolkit
> ... I can't do anything like dig into your code anytime soon.

I wouldnt' expect anythimg besides guidance when you are able to offer
it. It should be enough to allow me to learn how this works and utilize
it within it's design constraints. I do appreceate the feedback.

> Yep. I mainly work on the compiler and hosted mode integration,
> though recently I've also done a lot with RPC and JUnit, and a little
> with the UI framework. Come to think of it, I've worked on almost
> every piece of GWT. :D Which is why (especially with the upcoming
> release) I can only devote a small amount of time to answering your
> questions on this topic. I'm about to follow up to your next email
> with a couple of points, but I can't do anything like dig into your
> code anytime soon.

How big is the team?

claude

unread,
Aug 23, 2006, 11:10:45 AM8/23/06
to Google Web Toolkit
Scott Blum wrote:

> I don't know what you mean by "all matching types". For the record,
> you can only ever rebind one input class to one output class for the
> entire scope of one compilation. That means if your generator gets
> com.A as input and spits out com.B, com.A will ALWAYS resolve to com.B
> for the rest of the compile, and multiple GWT.create(com.A) will not
> cause your generator to run again. Does this break an assumption you
> were making?

In my mind, matching types are the types matched to fire off the
generator. So if the generator fires off when it sees a RemoteService,
I would consider the class that implements the interface to be a
matching type.

I have always assumed that a class can only have one proxy generated
for it. Although I can imagine cases where a generated class is not
necessarily a subclass used in substitution, though I have not explored
that avenue.

> To reiterate something I said before, you can never, ever directly
> refer a generated class name in source code. Except in the *one*
> special case where you generate a SET of files from one single
> Generator.generate() call. The set of files you generate from a
> single Generator.generate() call MUST be able to compile (e.g. javac)
> with only themselves and any non-generated source that may be
> available. It is only after the set of output files has been compiled
> without error that furthur rebinds can take place.

OK, this is a broken assumption on my part, and it confirms the
dependency graph issue I was asking about. So a single generated call
can create all the classes I want, and they can reference each other
explicitly since they are compiled together but each generator call
causes a javac compilation, so if I need to handle multiple proxies
that relate to each other atomically or use a different mechanism.

I'm not sure doing a graph analysis and handling proxy (substituted
subclasses) generation will work well for what I want to do now. I will
consider the notion of generating scafolding classes outside the beans
and still trigger the generator from a service-type interface at the
root of the graph. That should me more like the RPC model this process
was designed to handle. The scafolding can serialize/deserialize the
beans without having to subclass them.

Also, it's clear that for this particular application, I should be
leveraging the RPC code more, since it already has much of the
scafolding to do this, so I'll explore that too. Primarily, I have a
long-term but not a short-term need for this so I'm using it as an
excuse to explore and learn.

PS: Is there any reason at all that all generator calls could not be
made as a first pass through the source rather than inline? This would
make things a lot more flexible I suspect since javac compilation would
be deferred long enough for all generators to have run. I realize this
could be a big design change and that the working code is functional,
but I do think that it would improve on the design in significant ways.

Scott Blum

unread,
Aug 24, 2006, 7:38:08 PM8/24/06
to Google-We...@googlegroups.com
> PS: Is there any reason at all that all generator calls could not be
> made as a first pass through the source rather than inline? This would
> make things a lot more flexible I suspect since javac compilation would
> be deferred long enough for all generators to have run. I realize this
> could be a big design change and that the working code is functional,
> but I do think that it would improve on the design in significant ways.

Not really... in web mode we use the successful translation of a
source file into an AST to find the GWT.create() calls in the first
place. In hosted mode, we don't actually perform the rebind until
GWT.create() is called; that is, in hosted mode there is a real
GWT.create() method that really gets called. So your code has to be
compilable.

Scott

claude

unread,
Aug 27, 2006, 10:24:07 PM8/27/06
to Google Web Toolkit
> Not really... in web mode we use the successful translation of a
> source file into an AST to find the GWT.create() calls in the first
> place. In hosted mode, we don't actually perform the rebind until
> GWT.create() is called; that is, in hosted mode there is a real
> GWT.create() method that really gets called. So your code has to be
> compilable.
>
> Scott

Sorry I didn't answer sooner Scott. My question was more about whether
the GWT design was ideal, but I understand there are many requirements
to meet that probably prohibit changing that. I know I couldn't use
this strategy myself if it's not compatible with the existing
framework.

BTW: I've spent time this weekend using Velocity templates to generate
code and it seems to work better than writing inline code with println
or other coding abstractions when it comes to producing Java source
from the generate method..

I did have a questions, if you don't mind:

I finally got around to sudying the RPC user docs after not using it
directly for several months. I noticed that the primary service
interface, which inherits from RemoteService is cast into the Async
interface, which has a different signature and doesn't inherit from the
same parent. Can you briefly, in a few sentences, explain how that
actually works and what's permitted in terms of casting GWT.create()
results.

There be some smoke and mirrors going on here, me thinks ;-).

Scott Blum

unread,
Aug 31, 2006, 10:43:00 AM8/31/06
to Google-We...@googlegroups.com
On 8/27/06, claude <claude....@gmail.com> wrote:
> Sorry I didn't answer sooner Scott. My question was more about whether
> the GWT design was ideal, but I understand there are many requirements
> to meet that probably prohibit changing that. I know I couldn't use
> this strategy myself if it's not compatible with the existing
> framework.

Right, it's probably not changing in the near future. :)

> BTW: I've spent time this weekend using Velocity templates to generate
> code and it seems to work better than writing inline code with println
> or other coding abstractions when it comes to producing Java source
> from the generate method..

Thanks for the tip; generating classes with println can get a bit unweidly.

> I finally got around to sudying the RPC user docs after not using it
> directly for several months. I noticed that the primary service
> interface, which inherits from RemoteService is cast into the Async
> interface, which has a different signature and doesn't inherit from the
> same parent. Can you briefly, in a few sentences, explain how that
> actually works and what's permitted in terms of casting GWT.create()
> results.

The general contract is:
- If an interface is requested, GWT.create() should return an object
that implements that interface.
- If a class is requested, GWT.create() should return either the
requested class itself, or a subclass of the requested class.

However, GWT.create() is specified to return Object, so technically
you can return any class you want from a rebind. RPC violates the
general contract in this way by returning an object that implements
the Async version of the interface, even though the synchronous
version was requested. Ultimately, the key is that the user has to
know what to do with the returned object.

See this diagram for RPC specifics:
http://code.google.com/webtoolkit/documentation/com.google.gwt.doc.DeveloperGuide.RemoteProcedureCalls.PlumbingDiagram.html

Scott

claude

unread,
Aug 31, 2006, 11:53:55 AM8/31/06
to Google Web Toolkit
Scott Blum wrote:
> On 8/27/06, claude <claude....@gmail.com> wrote:
...

> The general contract is:
> - If an interface is requested, GWT.create() should return an object
> that implements that interface.
> - If a class is requested, GWT.create() should return either the
> requested class itself, or a subclass of the requested class.
>
> However, GWT.create() is specified to return Object, so technically
> you can return any class you want from a rebind. RPC violates the
> general contract in this way by returning an object that implements
> the Async version of the interface, even though the synchronous
> version was requested. Ultimately, the key is that the user has to
> know what to do with the returned object.
...
> Scott

When I look at the RPC Generator, I see that it calls the ProxyCreator
which returns the proxy class name. So, am I interpreting correctly
when I say that:

The GWT.create call with the RemoteService interface extension triggers
the ServiceInterfaceProxyGenerator, which uses ProxyCreate to create
the proxy interface and because it returns the new qualified class
name, that's what is available to GWT.create's return argument?

If that's the case, what happens if mutliple classes are returned from
the generate method. Or do I misunderstand this? If the generate method
creates multiple files, should only the one returned to GWT.create be
passed back as a return argument?

claude

unread,
Sep 5, 2006, 4:13:58 PM9/5/06
to Google Web Toolkit
His Scott,

I've been trying a few more things and have run into limitations I'm
not so clear on.

One thing I tried was to generate a proxy class (a subclass) that
declares an interface and to an interface that the subclass implements
doesn't appeart to work. When I try a GWT.getTypeName on the response
from GWT.create, it shows the class that was used as the GWT.create
argument, though it's clear the proxy class was generated and compiled
and the correct return value was provided to the generate method. In
this case, I was expecting the generated subclass to be returned. Is
that a reasonable expectation or am I still missing something?

The main question I had earlier, whether the return value from the
GWT.create should be the generate method's return value, is still of
interest. Here a re few more quickies:

Also, when you said that RPC doesn't strictly adhere to the GWT.create
contract did you mean I should avoid certain types of return values
because they may become impossible in the future?

I know you don't have a lot of time, but any information is always
welcome. I also realize I'm working on the bleading edge when exploring
these features. Still, there's no better way to learn than to jump
right in there and try things.

claude

unread,
Sep 5, 2006, 4:17:27 PM9/5/06
to Google Web Toolkit
The previous message got posted before I was finished editing. The
sencence "One thing I tried was to generate a proxy class (a subclass)
that
declares an interface and to an interface " is refering to a cast to
the interface.

The "Here a re few more quickies:" should be ignored...

Scott Blum

unread,
Sep 12, 2006, 4:15:42 AM9/12/06
to Google-We...@googlegroups.com
On 9/5/06, claude <claude....@gmail.com> wrote:
One thing I tried was to generate a proxy class (a subclass) that
declares an interface and to an interface that the subclass implements
doesn't appeart to work. When I try a GWT.getTypeName on the response
from GWT.create, it shows the class that was used as the GWT.create
argument, though it's clear the proxy class was generated and compiled
and the correct return value was provided to the generate method. In
this case, I was expecting the generated subclass to be returned. Is
that a reasonable expectation or am I still missing something?

Your generate method should return the name of the class you generated; in instance of that class will be returned from GWT.create().

Example:
GWT.create(package.Foo.class) -> generate("package.Foo")
generate returns "package.FooImpl" -> GWT.create() returns an instance of FooImpl

If you're returning the name of the correct subclass and not seeing the result you want, turn the logLevel to DEBUG and look through the rebind logs.

Also, when you said that RPC doesn't strictly adhere to the GWT.create
contract did you mean I should avoid certain types of return values
because they may become impossible in the future?

I just meant RPC isn't the prototypical example; kind of like how IdentityHashMap implements Map but doesn't adhere to the general contract for Map.

Scott

Scott Blum

unread,
Sep 21, 2006, 2:01:40 PM9/21/06
to claude, Google Web Toolkit
On 9/21/06, claude <claude....@gmail.com> wrote:
> At your request, I'm making sure you receive the questions, but I'm not
> sure how to post to the forum AND CC you using the online interface.

Thanks. I subscribe to the group via email, so I use Gmail rather
than Groups. :D

> At one point you mentioned that multiple class names could be returned.
> In retrospect I may have interpreted this differently than you
> intended. Are these two points valid:
>
> 1) The return value from the generate call should be the class name to
> be handed back to the GWT.create. Only one class should be returned,
> not a list.
>
> 2) Multiple classes/interfaces can be generated by the generate call
> and the compiler will pull in anything the primary class depends on.

Yes, my apologies! I was confused by what turned out to be an
obsolete comment at the end of
ServiceInterfaceProxyGenerator.generate(). You should only return a
single class name; the class to instantiate. You are correct on both
counts.

> > I just meant RPC isn't the prototypical example; kind of like how
> > IdentityHashMap implements Map but doesn't adhere to the general contract
> > for Map.
>

> Ok. Though my observation is that in all but the test case examples,
> GWT is generating classes from interfaces, which is the most common
> pattern.

Yes, that's correct. The reason RPC is kind of wonky is that, unlike
the other examples, the returned object --does not actually implement
the requested interface--. In other words, it's almost always correct
to write code like this:

Foo foo = (Foo)GWT.create(Foo.class).

That is the general contract for GWT.create(), that you can always
safely cast to the requested type. But for RPC, this is not valid,
this code will throw a ClassCastException:

FooService foo = (FooService)GWT.create(FooService.class)

instead you have to do:

FooServiceAsync foo = (FooServiceAsync)GWT.create(FooService.class)

because the returned object does not actually implement FooService.
This is not a pattern that should be emulated without a compelling
reason.

Scott

Scott Blum

unread,
Sep 22, 2006, 1:49:48 PM9/22/06
to claude, Google Web Toolkit
Hi claude,

Just to clarify, there's nothing at all wrong with casting the result
to another type. The only weirdness in RPC is that the object doesn't
implement the interface you originally requested at all. I suspect
that with most of the AOP stuff you'll want to do, the returned object
will generally implement/extend the requested type.

Actually, the main reason RPC doesn't work that way is historical.
The returned object used to support both synchronous and asynchronous
RPC. Unfortunately, the implications of synchronous RPC are so
horrific, we pulled out support for it.

At any rate, the important thing is that users of the generator know
what to expect from the results.
So don't worry, I'm sure we will continue to allow rebinds to return
whatever type they want.

Scott

On 9/22/06, claude <claude....@gmail.com> wrote:
> I should probably do this (I'm sure I'd be more sympathetic to mP's
> rants about reading documentation if I did ;-). I'll respond to you and
> assume you'll post again.
>
> I'd like to explore the likelyhood of having to break the model if I
> may.
>
> Generators are the compile-time equivalent to introspection, so I think
> they would serve much the same role, as applicable. Some areas in which
> introspection or meta data analysis are common:
>
> 1) AOP functionality, which involves extending the behavior or objects
> that already exist. These are typically implemented using byte code
> generation of proxy classes (where performance is less important, the
> standard proxy is used). Clearly generators could be used to do
> something similar.
>
> 2) IOC functionality is about wiring objects together to avoid
> coupling. Put another way (and I realize you know this but I'd rather
> articulate it for clarity), the idea is to avoid building object graphs
> inside a class (usually in constructors) and externalizing this so that
> graphs can be assembled without internal access to the class. This is
> typically done through interfaces and factories. Spring and other IOC
> containers use configuration files to define the factories using a
> data-driven approach.
>
> 3) Normal code generation is also a biggie and clealy in the GWT
> generator sweet spot. Existing frameworks like XDoclet and Middlegen
> take metadata and produce classes that address specific needs using
> this metadata. The catch with GWT is that classes that do not exist
> before hand must be castable to something where they are used.
>
> There are, of course, other applications, but these cover so many
> variants that they deserve more discussion.
>
> So, IOC is not a problem. Using factories to externalize injection is
> not too difficult in GWT. Using metadata to generate code is fine, so
> long as there is a clear entry point. I suspect creating objects that
> get wired together at compile time using externalized configuration is
> easy enough to do and, in fact, the module definition files tend to do
> this already.
>
> AOP and metadata-generated code, on the other hand, represent a
> potential conflicts with the (RPC) pattern you are recommending be
> avoided. I understand this motivation but clearly one of the first use
> cases GWT designers ran into had to break the model. I would like to
> encourage some discussion here and suggest that this use case is not
> unusual at all.
>
> In the RPC generator, we are returning a generated class that
> implements an alternate interface. Some AOP cases, such as loging
> method calls do not break the model. But if we want to add methods or
> generate new classes, they must be declared somewhere or the code will
> not be able to access them. As such, the pattern of declaring
> interfaces in advance seems like the only way to approach this. As a
> result, casting to an alternate type is inevitable in a large number of
> AOP and probably most metadata-related generation cases.
>
> My conclusion is that a very large number of use cases in which
> generators substitute for introspection (not all cases are applicable,
> of course) require casting to an alternate type. The generator
> mechanism is necessarily less flexible than introspection or byte code
> re-engineering but tends to address most cases very well I think. The
> main point, though, is that casting to an alternate type is not a
> marginal case at all and I suspect should not be discouraged.
>
> I say this largely to make sure this technique is supported in the long
> run and not considered bad practice or, I believe, we will be cut off
> from a large number of potentially valuable techniques that cannot be
> done using other means at the moment in GWT.
>
> As always, thanks for your comments and clarification. I'll do more
> experimentation over the weekend and ping you when I have learned a
> little more.

Reply all
Reply to author
Forward
0 new messages