PlaceHistoryManager

16 views
Skip to first unread message

Patrick Julien

unread,
Aug 21, 2010, 2:13:02 PM8/21/10
to google-web-tool...@googlegroups.com
I've been experimenting with PlaceHistoryManager and company. So far,
I have the following comments:

1. The tokens are big, they include the full path to the class, so all
packages and the name of the class. It's a lot. In our case, it also
exposes the name of the contractor to the user of the application.

2. Tokens in RequestFactory only work with Record based classes. I
think that's a shame. Will another facility supplement this? Because
if tokens from gwt will only support Record, why even bother with
ProxyPlace? Just make a goTo method in PlaceController that takes a
record. I would prefer to have tokens for a place directly. I think
that can be accomplished by having multiple place tokenizers however,
one per place.

3. Ultimately, I don't think the assumption that a record type is 1:1
to an activity. The mapper stuff allows for this but is yet more
code.

4. It's a lot harder than it seems to always have a record ready to
go. Especially coming from gwt-presenter were we always try to just
keep a numeric id.

5. I think the Place class getting support for a collection of arguments, i.e.,

setArgument(String, String);
String getArgument(String);
clearArguments();

and more importantly, parseArguments(String s); so that we can pass in
a string from the PlaceTokenizer.getPlace() after we've removed the
prefix would be most helpful and be applicable to most.

thank you,

Ray Cromwell

unread,
Aug 22, 2010, 12:37:48 AM8/22/10
to google-web-tool...@googlegroups.com
On Sat, Aug 21, 2010 at 11:13 AM, Patrick Julien <pju...@gmail.com> wrote:

1. The tokens are big, they include the full path to the class, so all
packages and the name of the class.  It's a lot.  In our case, it also
exposes the name of the contractor to the user of the application.


This is only temporarily, if you look, there's a class called SecurityProvider, eventually, all tokens will be mapped through this supporting arbitrary schemes. I believe we are looking at payload compression/minification for M4 or M5. I'll let Ray Ryan answer the others.
 

Patrick Julien

unread,
Aug 22, 2010, 8:56:15 AM8/22/10
to google-web-tool...@googlegroups.com
I am familiar with SecurityProvider but it's used from the server, not
place history. SecurityProvider works on the server to determine if
an operation is allowed, which is just checking for the @Service
annotation right now.

I am talking about the new methods in RequestFactory that provide
support for bookmarble locations based on Records. So for
UserAccountRecord, requestFactory.getToken(record); will produce a
string

com.yourcompany.records.UserAccountRecord-id

which is big

> --
> http://groups.google.com/group/Google-Web-Toolkit-Contributors

Ray Cromwell

unread,
Aug 23, 2010, 1:38:37 AM8/23/10
to google-web-tool...@googlegroups.com

Right, it's not implemented now, but planned. In M4+, all of these tokens will change from getClass().getName() to an obfuscated identifier (e.g. getToken(Foo.class) -> "xW"). The security provider implementation on the server will know how to decode these. I realize that, like pretty and short URLs, it would be nice go have compact history encoding, as well as options to control the look and feel if you want 'pretty/readable' tokens.  I think Ray Ryan's currently designed encoding is just a place holder.

-Ray
 

Thomas Broyer

unread,
Aug 23, 2010, 5:16:28 AM8/23/10
to Google Web Toolkit Contributors

On 21 août, 20:13, Patrick Julien <pjul...@gmail.com> wrote:
> I've been experimenting with PlaceHistoryManager and company.  So far,
> I have the following comments:

First, I've only read through the design-doc Wave and the code, I
haven't yet experimented with it (as I first have to migrate my
project from M2 to M3)

> 1. The tokens are big, they include the full path to the class, so all
> packages and the name of the class.  It's a lot.  In our case, it also
> exposes the name of the contractor to the user of the application.

This is only the "default" Proxy{,List}Place.Tokenizer
implementations; I put "default" between quotes as it's not even a
default, you have to *explicitly* use the tokenizers (the scaffold app
does this implicitly by using a PlaceHistoryHandlerWithFactory which
analyzes the so-called factory class looking for methods taking no
argument and returning PlaceTokenizer instances).
It looks like you're absolutely free to use any PlaceTokenizer
implementation you want, even with the stock Proxy{,List}Place places.

> 2. Tokens in RequestFactory only work with Record based classes.  I
> think that's a shame.  Will another facility supplement this?  Because
> if tokens from gwt will only support Record, why even bother with
> ProxyPlace?  Just make a goTo method in PlaceController that takes a
> record.  I would prefer to have tokens for a place directly.  I think
> that can be accomplished by having multiple place tokenizers however,
> one per place.

I don't understand. Of course RequestFactory only deals with Records,
but that's totally independent from Places and History tokens (well,
RequestFactory can give you tokens for your records, but yet again,
that's only if you *want* to use that, which you'll do by using the
provided Proxy{,List}Place.Tokenizer).
I haven't seen any limitation in PlaceHistoryHandler forbidding its
use with places other the the provided Proxy{,List}Place; of course,
for those places, you'd have to provide your own PlaceTokenizer.

> 3. Ultimately, I don't think the assumption that a record type is 1:1
> to an activity.  The mapper stuff allows for this but is yet more
> code.

There's no such assumption AFAICT. And actually, for a given record
type, ProxyPlace gives you 3 different places, and ProxyListPlace an
additional one. You're then free to map those places to activities in
your ActivityMapper. And of course you're free to not use Proxy{,List}
Place at all.

> 4. It's a lot harder than it seems to always have a record ready to
> go.  Especially coming from gwt-presenter were we always try to just
> keep a numeric id.

I don't understand. (maybe because I don't know gwt-presenter? but the
last time I looked at it, I really disliked its implementation of
places; it didn't "get" the concept IMO)

> 5. I think the Place class getting support for a collection of arguments, i.e.,
>
> setArgument(String, String);
> String getArgument(String);
> clearArguments();
>
> and more importantly, parseArguments(String s); so that we can pass in
> a string from the PlaceTokenizer.getPlace() after we've removed the
> prefix would be most helpful and be applicable to most.

Please NOOOO!!! That's the role of the PlaceTokenizer. If you want a
generic place with String "arguments" and a generic tokenizer making
look like e.g. a query-string in history tokens, you can do it already
but please don't make it a default!
You shouldn't do getArgument("foo"), you should instead have a
specific Place subclass giving you a getFoo().
It may sound a bit harsh but you've IMO been very badly influenced by
gwt-presenter.

Patrick Julien

unread,
Aug 23, 2010, 8:49:18 AM8/23/10
to google-web-tool...@googlegroups.com
On Mon, Aug 23, 2010 at 5:16 AM, Thomas Broyer <t.br...@gmail.com> wrote:
>
> On 21 août, 20:13, Patrick Julien <pjul...@gmail.com> wrote:
>> I've been experimenting with PlaceHistoryManager and company.  So far,
>> I have the following comments:
>
> First, I've only read through the design-doc Wave and the code, I
> haven't yet experimented with it (as I first have to migrate my
> project from M2 to M3)
>
>> 1. The tokens are big, they include the full path to the class, so all
>> packages and the name of the class.  It's a lot.  In our case, it also
>> exposes the name of the contractor to the user of the application.
>
> This is only the "default" Proxy{,List}Place.Tokenizer
> implementations; I put "default" between quotes as it's not even a
> default, you have to *explicitly* use the tokenizers (the scaffold app
> does this implicitly by using a PlaceHistoryHandlerWithFactory which
> analyzes the so-called factory class looking for methods taking no
> argument and returning PlaceTokenizer instances).
> It looks like you're absolutely free to use any PlaceTokenizer
> implementation you want, even with the stock Proxy{,List}Place places.

No it's not. This is the tokens returns by RequestFactory based on
your record classes. The *Place objects that you use have no bearing
on what these methods produce. The place tokenizer has nothing to do
with this other that it uses RequestFactory to get these tokens and
get back a Class<?> back from the tokens.

>
>> 2. Tokens in RequestFactory only work with Record based classes.  I
>> think that's a shame.  Will another facility supplement this?  Because
>> if tokens from gwt will only support Record, why even bother with
>> ProxyPlace?  Just make a goTo method in PlaceController that takes a
>> record.  I would prefer to have tokens for a place directly.  I think
>> that can be accomplished by having multiple place tokenizers however,
>> one per place.
>
> I don't understand. Of course RequestFactory only deals with Records,
> but that's totally independent from Places and History tokens (well,

Why "of course"? RequestFactory means a factory of "requests", not a
factory of "records". You can use RequestFactory to fire requests
that don't have any records, e.g.,

RequestObject<Long> getMeALongNotUsingARecord(Long param1, Long param2);

no request here.


> RequestFactory can give you tokens for your records, but yet again,
> that's only if you *want* to use that, which you'll do by using the
> provided Proxy{,List}Place.Tokenizer).


Again, hence my objection.


> I haven't seen any limitation in PlaceHistoryHandler forbidding its
> use with places other the the provided Proxy{,List}Place; of course,
> for those places, you'd have to provide your own PlaceTokenizer.

That's what I am currently doing.

>
>> 3. Ultimately, I don't think the assumption that a record type is 1:1
>> to an activity.  The mapper stuff allows for this but is yet more
>> code.
>
> There's no such assumption AFAICT. And actually, for a given record
> type, ProxyPlace gives you 3 different places, and ProxyListPlace an
> additional one. You're then free to map those places to activities in
> your ActivityMapper. And of course you're free to not use Proxy{,List}
> Place at all.


There's is such an assumption in ProxyPlace because it requires a
record but more to the point because of AbstractRecordEditActivity.
If you have a user record and you want to edit your record, your user
record edit activity should be a function of your user privileges.
Currently, the only way to do this is to use activity mappers or use
multiple places. Again, I stated this.

The other reason why that assumption is made is that
AbstractRecordEditActivity requires a record now. Currently, there is
no way to build a proxy record, i.e., a record where all fields are
stale but the id, in code. You would need to create a string token
manually which assumes that:

a) You know what the string token looks like, append the id, and pass
it on to RequestFactory.
b) The string token algorithm will not change from version to version.
Already on this thread, we can see that it will already change.
c) or, fire the list request before you create the
AbstractRecordEditActivity, get a fresh record from the server and
have AbstractRecordEditActivity fire a list yet again once it's
constructed.

Due to AbstractRecordEditActivity, we're pretty much forced to carry
around records in the place.


>
>> 5. I think the Place class getting support for a collection of arguments, i.e.,
>>
>> setArgument(String, String);
>> String getArgument(String);
>> clearArguments();
>>
>> and more importantly, parseArguments(String s); so that we can pass in
>> a string from the PlaceTokenizer.getPlace() after we've removed the
>> prefix would be most helpful and be applicable to most.
>
> Please NOOOO!!! That's the role of the PlaceTokenizer. If you want a
> generic place with String "arguments" and a generic tokenizer making
> look like e.g. a query-string in history tokens, you can do it already
> but please don't make it a default!
> You shouldn't do getArgument("foo"), you should instead have a
> specific Place subclass giving you a getFoo().


Because you're assuming that one place and one list place is
sufficient for all needs and I don't think it is. You have common
code dealing with arguments that will always be there. We can have a
base place that adds it and does it on our own but would still be
practical to have in Place since it's currently empty.

The argument for these common methods for arguments is that at the
very least, they have to be placed on the url bar and be parsed back.
That is common to all places.

Furthermore, yes, getFoo() instead of getArgument("foo") is nice but

a) It doesn't mean a base place with getArgument("foo") can't have a
concrete class that has getFoo() that does the proper construction
b) With ActivityMapper no longer being a generic, getting to getFoo()
means downcasting all over the code.

To address your comment that this is the role of PlaceTokenizer, it
is, but currently, there is no standard PlaceTokenizer that handles
standard query parameters. And there can't be one unless it has an
interface in which it get and set place arguments into the place
itself, hence the argument for get/setArgument in Place.

> It may sound a bit harsh but you've IMO been very badly influenced by
> gwt-presenter.


Is that really necessary? We're building out a real, big application
and giving back feedback.

Thomas Broyer

unread,
Aug 23, 2010, 12:07:50 PM8/23/10
to Google Web Toolkit Contributors

On 23 août, 14:49, Patrick Julien <pjul...@gmail.com> wrote:
> On Mon, Aug 23, 2010 at 5:16 AM, Thomas Broyer <t.bro...@gmail.com> wrote:
>
> > On 21 août, 20:13, Patrick Julien <pjul...@gmail.com> wrote:
> >> I've been experimenting with PlaceHistoryManager and company.  So far,
> >> I have the following comments:
>
> > First, I've only read through the design-doc Wave and the code, I
> > haven't yet experimented with it (as I first have to migrate my
> > project from M2 to M3)
>
> >> 1. The tokens are big, they include the full path to the class, so all
> >> packages and the name of the class.  It's a lot.  In our case, it also
> >> exposes the name of the contractor to the user of the application.
>
> > This is only the "default" Proxy{,List}Place.Tokenizer
> > implementations; I put "default" between quotes as it's not even a
> > default, you have to *explicitly* use the tokenizers (the scaffold app
> > does this implicitly by using a PlaceHistoryHandlerWithFactory which
> > analyzes the so-called factory class looking for methods taking no
> > argument and returning PlaceTokenizer instances).
> > It looks like you're absolutely free to use any PlaceTokenizer
> > implementation you want, even with the stock Proxy{,List}Place places.
>
> No it's not.  This is the tokens returns by RequestFactory based on
> your record classes.  The *Place objects that you use have no bearing
> on what these methods produce.  The place tokenizer has nothing to do
> with this other that it uses RequestFactory to get these tokens and
> get back a Class<?> back from the tokens.

I'm sorry but I don't understand.
1. Ray C said that the goal was that there is some obfuscation going
on, to be added later on what RequestFactory.getToken/getProxy returns/
expects.
2. I added that this is only used by ProxyPlace.Tokenizer (and I
really see RequestFactory.getToken/getProxy as helper methods for
ProxyPlace.Tokenizer), that you're free to *not* use.
I understand this is feedback on RequestFactory.getToken return values
(though it wasn't very clear) but yet again, Ray C responded to your
complaint and in case you'd like to deliver a version of your app
without waiting for this to be fixed/enhanced, you can workaround it
by providing your own PlaceTokenizer.

> >> 2. Tokens in RequestFactory only work with Record based classes.  I
> >> think that's a shame.  Will another facility supplement this?  Because
> >> if tokens from gwt will only support Record, why even bother with
> >> ProxyPlace?  Just make a goTo method in PlaceController that takes a
> >> record.  I would prefer to have tokens for a place directly.  I think
> >> that can be accomplished by having multiple place tokenizers however,
> >> one per place.
>
> > I don't understand. Of course RequestFactory only deals with Records,
> > but that's totally independent from Places and History tokens (well,
>
> Why "of course"?  RequestFactory means a factory of "requests", not a
> factory of "records".  You can use RequestFactory to fire requests
> that don't have any records, e.g.,
>
> RequestObject<Long> getMeALongNotUsingARecord(Long param1, Long param2);
>
> no [record] here.

Yes, you can, but (correct me if I'm wrong):
1. anything more complex than a primitive type or its wrapper class, a
Date, or a String has to be a Record
2. you'd have to make your own Place and PlaceTokenizer for those, so
why bother asking RequestFactory for a token? (have I said I do think
getToken/getProxy are really "just" helpers for
ProxyPlace.Tokenizer? ;-) )
You used the expression "tokens from GWT", which I understood as
"history management", which *of course* are not limited to records; so
there's really a reason to "bother" with ProxyPlace.
Having a goTo(Record) in PlaceController would make it depend on
RequestFactory, which would be bad design (as long as you don't use
ProxyPlace and ProxyListPlace, you can very well use PlaceController
and PlaceHistoryHandler without using RequestFactory at all).
I'd add that having a PlaceTokenizer *per place* is by-design; this is
very different from gwt-presenter which has a single PlaceFormatter to
handle all places.

> > I haven't seen any limitation in PlaceHistoryHandler forbidding its
> > use with places other the the provided Proxy{,List}Place; of course,
> > for those places, you'd have to provide your own PlaceTokenizer.
>
> That's what I am currently doing.

have you tried using ProxyPlace (provided what it represents –an
"operation" on a particular record– suits your needs) with a custom
PlaceTokenizer?

> >> 3. Ultimately, I don't think the assumption that a record type is 1:1
> >> to an activity.  The mapper stuff allows for this but is yet more
> >> code.
>
> > There's no such assumption AFAICT. And actually, for a given record
> > type, ProxyPlace gives you 3 different places, and ProxyListPlace an
> > additional one. You're then free to map those places to activities in
> > your ActivityMapper. And of course you're free to not use Proxy{,List}
> > Place at all.
>
> There's is such an assumption in ProxyPlace because it requires a
> record

ProxyPlace (as I said above) represents "an operation on a record", so
it of course requires a record. Not all places are record-related, but
in this case you'd use another Place than a ProxyPlace.
Let's say you have a "dashboard" as your "home page", you'll then
create a DashboardPlace and its associated PlaceTokenizer. If you also
have an "about page", you'll create an AboutPlace and its associated
PlaceTokenizer, etc. (note that technically, you could also use a
single PlaceTokenizer for multiple places if you liked)
A Place answers the question "where am I?", a ProxyPlace is an answer
of the form "doing something on this record", where "doing something"
can be "creating", "seeing the details" or "editing", and "this
record" being a record instance; and a ProxyListPlace is an answer of
the form "a list of records of type X". Again, this is different from
gwt-presenter (where you use a singleton Place per presenter –speaking
from the examples, I cannot understand the exact flow of events/
actions in gwt-presenter "trunk"–, and where a Place instance actually
represents "some screen/activity", and the place has some added
"state" the describes the actual "data" being displayed/edited/
whatever).

> The other reason why that assumption is made is that
> AbstractRecordEditActivity requires a record now.  Currently, there is
> no way to build a proxy record, i.e., a record where all fields are
> stale but the id, in code.

Yes you can: use requestFactory.create()
http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/gwt/app/place/AbstractRecordListActivity.java#125

> >> 5. I think the Place class getting support for a collection of arguments, i.e.,
>
> >> setArgument(String, String);
> >> String getArgument(String);
> >> clearArguments();
>
> >> and more importantly, parseArguments(String s); so that we can pass in
> >> a string from the PlaceTokenizer.getPlace() after we've removed the
> >> prefix would be most helpful and be applicable to most.
>
> > Please NOOOO!!! That's the role of the PlaceTokenizer. If you want a
> > generic place with String "arguments" and a generic tokenizer making
> > look like e.g. a query-string in history tokens, you can do it already
> > but please don't make it a default!
> > You shouldn't do getArgument("foo"), you should instead have a
> > specific Place subclass giving you a getFoo().
>
> Because you're assuming that one place and one list place is
> sufficient for all needs and I don't think it is.

Neither do I!

> You have common
> code dealing with arguments that will always be there.  We can have a
> base place that adds it and does it on our own but would still be
> practical to have in Place since it's currently empty.
>
> The argument for these common methods for arguments is that at the
> very least, they have to be placed on the url bar and be parsed back.
> That is common to all places.
>
> Furthermore, yes, getFoo() instead of getArgument("foo") is nice but
>
> a) It doesn't mean a base place with getArgument("foo") can't have a
> concrete class that has getFoo() that does the proper construction
> b) With ActivityMapper no longer being a generic, getting to getFoo()
> means downcasting all over the code.

Yes, ActivityMappers are expected to be made of if/else around
instanceof tests. And actually, Places are only expected to be used
from ActivityMappers (the ActivityMapper will setup the Activity with
the appropriate "state", so an Activity doesn't need to know the
Place, and I'd even say it shouldn't know about places at all, except
for instantiating them to give them to PlaceController.goTo).
See for example
http://code.google.com/p/google-web-toolkit/source/browse/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesDetailsActivities.java
(which only deals with ProxyPlace, but still does a check at the very
start to make sure it doesn't deal with anything else; and the
ExpensesEntityTypesProcessor (this is an implementation detail of apps
generated by Roo; I guess it makes them easier to maintain by Roo when
you add record types) does the same with the record types:
http://code.google.com/p/google-web-toolkit/source/browse/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpensesEntityTypesProcessor.java#67

> To address your comment that this is the role of PlaceTokenizer, it
> is, but currently, there is no standard PlaceTokenizer that handles
> standard query parameters.

And I don't think there should be. Though I think it should be easy to
build one, and "frameworks" like gwt-presenter would probably given
one to you.


> And there can't be one unless it has an
> interface in which it get and set place arguments into the place
> itself, hence the argument for get/setArgument in Place.

public class GenericPlace extends Place {
private final String name;
private final Map<String,String> parameters;

// ctor and getters here...

public static class Tokenizer extends PlaceTokenizer<GenericPlace> {
public GenericPlace getPlace(String token) {
// parse the token and return a new GenericPlace(...)
}
public String getToken(GenericPlace place) {
// build the <name>?<key1>=<value1>&<key2>=<value2>&... from the
GenericPlace
}
}
}

Now, you just have to use GenericPlace all over the place (and yes, it
means starting your ActivityMappers with an "if (!(place isntanceof
GenericPlace)) return;" statement) and code everything in a non-type-
safe manner with string comparisons everywhere.

The intended use is that you discriminate the kinds of places based on
a token prefix, so GWT takes care of the dispatch and only calls the
"appropriate" PlaceTokenizer for a given kind of Place. You can of
course do your own dispatching in a single "root" (unprefixed)
PlaceTokenizer, but that means (a little bit) more code.
And on serializing a Place to a token, GWT will match the
PlaceTokenizer based on its generic type argument (code generation
FTW!)

> > It may sound a bit harsh but you've IMO been very badly influenced by
> > gwt-presenter.
>
> Is that really necessary?  We're building out a real, big application
> and giving back feedback.

I'm sorry, this was more directed to gwt-presenter than to you
personnaly. As I said, I really dislike how gwt-presenter deals with
places, and I think it doesn't "get" the concept of places.
The reason I'm supporting GWT's approach is that the one I developed a
year ago for our own apps is very similar (minus the notion of
activities, but at the lower-level of Place, PlaceController,
PlaceChangeEvent, and now PlaceTokenizer, there are many things in
common).

Patrick Julien

unread,
Aug 23, 2010, 1:17:49 PM8/23/10
to google-web-tool...@googlegroups.com
On Mon, Aug 23, 2010 at 12:07 PM, Thomas Broyer <t.br...@gmail.com> wrote:
>> > implementation you want, even with the stock Proxy{,List}Place places.
>>
>> No it's not.  This is the tokens returns by RequestFactory based on
>> your record classes.  The *Place objects that you use have no bearing
>> on what these methods produce.  The place tokenizer has nothing to do
>> with this other that it uses RequestFactory to get these tokens and
>> get back a Class<?> back from the tokens.
>
> I'm sorry but I don't understand.
> 1. Ray C said that the goal was that there is some obfuscation going
> on, to be added later on what RequestFactory.getToken/getProxy returns/
> expects.

I know, I responded to your comment.


> 2. I added that this is only used by ProxyPlace.Tokenizer (and I
> really see RequestFactory.getToken/getProxy as helper methods for
> ProxyPlace.Tokenizer), that you're free to *not* use.
> I understand this is feedback on RequestFactory.getToken return values
> (though it wasn't very clear) but yet again, Ray C responded to your
> complaint and in case you'd like to deliver a version of your app
> without waiting for this to be fixed/enhanced, you can workaround it
> by providing your own PlaceTokenizer.

I don't see what this has to do with anything. We do have our own
place tokenizer but it doesn't mean there isn't value in being able to
get a history token from RequestFactory for something that isn't a
record. Right now, anything that has a getId() would be sufficient


>
>> >> 2. Tokens in RequestFactory only work with Record based classes.  I
>> >> think that's a shame.  Will another facility supplement this?  Because
>> >> if tokens from gwt will only support Record, why even bother with
>> >> ProxyPlace?  Just make a goTo method in PlaceController that takes a
>> >> record.  I would prefer to have tokens for a place directly.  I think
>> >> that can be accomplished by having multiple place tokenizers however,
>> >> one per place.
>>
>> > I don't understand. Of course RequestFactory only deals with Records,
>> > but that's totally independent from Places and History tokens (well,
>>
>> Why "of course"?  RequestFactory means a factory of "requests", not a
>> factory of "records".  You can use RequestFactory to fire requests
>> that don't have any records, e.g.,
>>
>> RequestObject<Long> getMeALongNotUsingARecord(Long param1, Long param2);
>>
>> no [record] here.
>
> Yes, you can, but (correct me if I'm wrong):
> 1. anything more complex than a primitive type or its wrapper class, a
> Date, or a String has to be a Record

I would use a Record yes but it doesn't have to be.

> 2. you'd have to make your own Place and PlaceTokenizer for those, so
> why bother asking RequestFactory for a token? (have I said I do think
> getToken/getProxy are really "just" helpers for
> ProxyPlace.Tokenizer? ;-) )


But why should they be limited to ProxyPlace? You understand this is
a toolkit and it's trying to do work that we would otherwise need to
do? If they are are helpers only for Proxy*Place and only
Proxy*Place, why make them so visible by having them in
RequestFactory?

> You used the expression "tokens from GWT", which I understood as
> "history management", which *of course* are not limited to records; so
> there's really a reason to "bother" with ProxyPlace.
> Having a goTo(Record) in PlaceController would make it depend on
> RequestFactory, which would be bad design (as long as you don't use
> ProxyPlace and ProxyListPlace, you can very well use PlaceController
> and PlaceHistoryHandler without using RequestFactory at all).
> I'd add that having a PlaceTokenizer *per place* is by-design; this is
> very different from gwt-presenter which has a single PlaceFormatter to
> handle all places.


A place tokenizer per place is a lot of boiler plate code. Even if
you try to minimize the impact. Again, it's clear to me that the goal
of 2.1 is developer productivity. Hence, I don't think this follows
the spirit of it.

The comment on goTo taken a record is not me asking you to explain to
me why it's bad design or me asking to actually have goTo take a
record. I'm announcing it to make a point, where things have been
made too narrow for real usage.

>
>> > I haven't seen any limitation in PlaceHistoryHandler forbidding its
>> > use with places other the the provided Proxy{,List}Place; of course,
>> > for those places, you'd have to provide your own PlaceTokenizer.
>>
>> That's what I am currently doing.
>
> have you tried using ProxyPlace (provided what it represents –an
> "operation" on a particular record– suits your needs) with a custom
> PlaceTokenizer?


I did and failed. ProxyPlace means assuming 1:1 between record and
activity. We have a user record. So edit on a user record means
either my preferences, other user or patient. Again, before you
repeat what I said before in a different way, I did state this and
stated how you could accomplish this using ProxyPlace and
ActivityMapper. The feedback was that it's too cumbersome to do it
this way hence we continued to use multiple places.

I don't know what you're trying to say here, I'm not even going to try
to respond, it's too long

>
>> The other reason why that assumption is made is that
>> AbstractRecordEditActivity requires a record now.  Currently, there is
>> no way to build a proxy record, i.e., a record where all fields are
>> stale but the id, in code.
>
> Yes you can: use requestFactory.create()
> http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/gwt/app/place/AbstractRecordListActivity.java#125


This produces a record who's id is set to a future from the future id
generator. You have no way of setting the value of the id of the
newly created record.


>
> Yes, ActivityMappers are expected to be made of if/else around
> instanceof tests. And actually, Places are only expected to be used


and that is to be considered good design?


> from ActivityMappers (the ActivityMapper will setup the Activity with
> the appropriate "state", so an Activity doesn't need to know the
> Place, and I'd even say it shouldn't know about places at all, except
> for instantiating them to give them to PlaceController.goTo).
> See for example
> http://code.google.com/p/google-web-toolkit/source/browse/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesDetailsActivities.java
> (which only deals with ProxyPlace, but still does a check at the very
> start to make sure it doesn't deal with anything else; and the
> ExpensesEntityTypesProcessor (this is an implementation detail of apps
> generated by Roo; I guess it makes them easier to maintain by Roo when
> you add record types) does the same with the record types:
> http://code.google.com/p/google-web-toolkit/source/browse/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ExpensesEntityTypesProcessor.java#67
>

Again, I don't know why you keep me pointed out to the basics
considering I told you we have a large app that we're building with
GWT head.


>> To address your comment that this is the role of PlaceTokenizer, it
>> is, but currently, there is no standard PlaceTokenizer that handles
>> standard query parameters.
>
> And I don't think there should be. Though I think it should be easy to
> build one, and "frameworks" like gwt-presenter would probably given
> one to you.
>

Again, you do understand that GWT is a framework? We've written one,
the feedback is that I believe it should be provided, feel free to
disagree.


again, snipping more long things,

>
>> > It may sound a bit harsh but you've IMO been very badly influenced by
>> > gwt-presenter.
>>
>> Is that really necessary?  We're building out a real, big application
>> and giving back feedback.
>
> I'm sorry, this was more directed to gwt-presenter than to you
> personnaly. As I said, I really dislike how gwt-presenter deals with
> places, and I think it doesn't "get" the concept of places.
> The reason I'm supporting GWT's approach is that the one I developed a
> year ago for our own apps is very similar (minus the notion of
> activities, but at the lower-level of Place, PlaceController,
> PlaceChangeEvent, and now PlaceTokenizer, there are many things in
> common).


So that makes it OK to ridicule the hard work that the gwt-presenter
maintainer did? He stepped up after Google I/O 2009 and that's a lot
more than most people can say and just for the sake of full
disclosure, I do not know him. Furthermore, it helped guide the
design of gwt 2.1 by providing use cases of what people were doing and
trying to do. So far, I feel the place management work in GWT 2.1 is
good but could nonetheless gain insight into these comments and
gwt-presenter. I would also say the large if-else statements of
instanceof on places is not something I look forward to and have
adopted a visitor pattern to take that out of the equation. In any
case, feel free to continue this thread but I'm done.

Thomas Broyer

unread,
Aug 24, 2010, 8:51:45 AM8/24/10
to Google Web Toolkit Contributors

From your first message(s), I had the impression you wanted the GWT
2.1 places to look more like gwt-presenter than what they currently
are.
When I said gwt-presenter doesn't "get" the concept of places, maybe I
should have said it doesn't have the same definition of what is a
"place", or at least that the Place object in gwt-presenter doesn't
represent the same thing as the Place objects in GWT 2.1.

> >> >> 2. Tokens in RequestFactory only work with Record based classes.  I
> >> >> think that's a shame.  Will another facility supplement this?  Because
> >> >> if tokens from gwt will only support Record, why even bother with
> >> >> ProxyPlace?  Just make a goTo method in PlaceController that takes a
> >> >> record.  I would prefer to have tokens for a place directly.  I think
> >> >> that can be accomplished by having multiple place tokenizers however,
> >> >> one per place.
>
> >> > I don't understand. Of course RequestFactory only deals with Records,
> >> > but that's totally independent from Places and History tokens (well,
>
> >> Why "of course"?  RequestFactory means a factory of "requests", not a
> >> factory of "records".  You can use RequestFactory to fire requests
> >> that don't have any records, e.g.,
>
> >> RequestObject<Long> getMeALongNotUsingARecord(Long param1, Long param2);
>
> >> no [record] here.
>
> > Yes, you can, but (correct me if I'm wrong):
> > 1. anything more complex than a primitive type or its wrapper class, a
> > Date, or a String has to be a Record
>
> I would use a Record yes but it doesn't have to be.

As far as I can tell, it *has* to be:
Return type (both client-side and server-side, here's client-side;
enforced at compile-time):
http://code.google.com/p/google-web-toolkit/source/browse/tags/2.1.0-ms3/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java#551
return type, server-side:
Parameter types (only enforced on the server-side though; will
use .toString() on the client-side when sending the request):
http://code.google.com/p/google-web-toolkit/source/browse/tags/2.1.0-ms3/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java#533

> > 2. you'd have to make your own Place and PlaceTokenizer for those, so
> > why bother asking RequestFactory for a token? (have I said I do think
> > getToken/getProxy are really "just" helpers for
> > ProxyPlace.Tokenizer? ;-) )
>
> But why should they be limited to ProxyPlace?  You understand this is
> a toolkit and it's trying to do work that we would otherwise need to
> do?  If they are are helpers only for Proxy*Place and only
> Proxy*Place, why make them so visible by having them in
> RequestFactory?

Because only RequestFactory knows all record types, and they have to
be public API for Proxy*Place to be able to call them (well,
Proxy*Place could also cast the RequestFactory to
RequestFactoryJsonImpl and getToken/getProxy only be defined there and
not on the RequestFactory interface)

> > I'd add that having a PlaceTokenizer *per place* is by-design; this is
> > very different from gwt-presenter which has a single PlaceFormatter to
> > handle all places.
>
> A place tokenizer per place is a lot of boiler plate code.  Even if
> you try to minimize the impact.  Again, it's clear to me that the goal
> of 2.1 is developer productivity.  Hence, I don't think this follows
> the spirit of it.

I came to the same approach of a PlaceTokenizer per Place for my own
implementation (well, actually it's less restricted, but you generally
end up doing just that: writing a PlaceTokenizer per place), because
each Place needed specific code. If all your places can share the same
parsing/serialization code, you should (as in "GWT should let you do
that") be able to use a single PlaceTokenizer to process them all.
If you want to see where I'm coming from, I have a Google Docs that
explains what I did (with the details of each iteration in the design
process) that I can share with you (it's a draft for a blogpost that I
started a while ago and never finished, so I'm reluctant to making it
public).

(looking at the code more closely, I'm not 100% OK with how it's done:
tokens are necessarily of the form <prefix>:<tokenizer-specific-data>,
and you cannot have a tokenizer called for the "no prefix found" case;
fortunately, the code is rather small, so if you don't want to live
with these limitations, you can easily switch the PlaceHistoryHandler
generator for your own)

> >> > I haven't seen any limitation in PlaceHistoryHandler forbidding its
> >> > use with places other the the provided Proxy{,List}Place; of course,
> >> > for those places, you'd have to provide your own PlaceTokenizer.
>
> >> That's what I am currently doing.
>
> > have you tried using ProxyPlace (provided what it represents –an
> > "operation" on a particular record– suits your needs) with a custom
> > PlaceTokenizer?
>
> I did and failed.  ProxyPlace means assuming 1:1 between record and
> activity.  We have a user record.  So edit on a user record means
> either my preferences, other user or patient.  Again, before you
> repeat what I said before in a different way, I did state this and
> stated how you could accomplish this using ProxyPlace and
> ActivityMapper.  The feedback was that it's too cumbersome to do it
> this way hence we continued to use multiple places.

Oh, yes, as I explained, ProxyPlace represents "an operation on a
record" with a limited set of possible operations, so if you need more
operations than "create record", "see record details" and "edit
record" (such as "edit some part of the record" and "edit some other
part of the record") then ProxyPlace isn't for you.

> >> The other reason why that assumption is made is that
> >> AbstractRecordEditActivity requires a record now.  Currently, there is
> >> no way to build a proxy record, i.e., a record where all fields are
> >> stale but the id, in code.
>
> > Yes you can: use requestFactory.create()
> >http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/...
>
> This produces a record who's id is set to a future from the future id
> generator.  You have no way of setting the value of the id of the
> newly created record.

Oh, yes, right. Maybe there should be a
RequestFactory.getProxy(Class<? extends Record>, Object id) (FYI, the
ID was a String in M1 and M2 and is now a Long in M3, but is expected
to be of any type you want in the final release)

> >> To address your comment that this is the role of PlaceTokenizer, it
> >> is, but currently, there is no standard PlaceTokenizer that handles
> >> standard query parameters.
>
> > And I don't think there should be. Though I think it should be easy to
> > build one, and "frameworks" like gwt-presenter would probably given
> > one to you.
>
> Again, you do understand that GWT is a framework?  We've written one,
> the feedback is that I believe it should be provided, feel free to
> disagree.

GWT isn't a framework, it's a toolkit. Frameworks like gwt-presenter
and gwt-platform will, I believe, continue to exist, based on the new
Place/Activity or not.

> >> > It may sound a bit harsh but you've IMO been very badly influenced by
> >> > gwt-presenter.
>
> >> Is that really necessary?  We're building out a real, big application
> >> and giving back feedback.
>
> > I'm sorry, this was more directed to gwt-presenter than to you
> > personnaly. As I said, I really dislike how gwt-presenter deals with
> > places, and I think it doesn't "get" the concept of places.
> > The reason I'm supporting GWT's approach is that the one I developed a
> > year ago for our own apps is very similar (minus the notion of
> > activities, but at the lower-level of Place, PlaceController,
> > PlaceChangeEvent, and now PlaceTokenizer, there are many things in
> > common).
>
> So that makes it OK to ridicule the hard work that the gwt-presenter
> maintainer did?  He stepped up after Google I/O 2009 and that's a lot
> more than most people can say and just for the sake of full
> disclosure, I do not know him.

Uh! ridicule?! I think I expressed my opinions about gwt-presenter,
and "places" in general, publicly, a year ago, so its maintainer knows
what I think about it, which is that I really don't like the way it's
done (and to begin with, that there's no need of a "framework" to go
MVP, and that "places" are orthogonal to MVP, but that's another
debate).
That being said, gwt-presenter's approach is *very* different from the
one in GWT 2.1, and all I keep saying is that you shouldn't try to
compare them.
In particular, (I think) one shouldn't ask "I did this that way with
project X, please add the same to GWT proper", he should rather say
either "I have a particular need Y that doesn't fit in GWT 2.1's
approach, could GWT be enhanced to answer my need?" (you can have a
look to the RequestFactory wave to see how the design changed in
response to such feedback) or "I have a need Y that I can code with
GWT 2.1 but it's overly complicated, could GWT be enhanced so it's
easier? for reference, it was much easier with project X, where I did
blah blah blah..."
Reply all
Reply to author
Forward
0 new messages