Ryan:
Hello. My name is Ray.
And I'll be talking
to you today
about GWT application
architecture best practices.
We tried to find a drier title,
but this is the one that won.
I am stunned, amazed,
and flattered
that you all are here
when you could be eating lunch
or learning about Wave,
so thank you very much
for showing up.
I was going to go
get lunch myself if you hadn't.
So, I'll warn you also
that all of my rehearsals
of this have run long.
And I've responded to that
by adding more slides
to the presentation,
so I'm probably going to do
a lot of mumbling
and a lot of talking fast.
And we'll see where it goes.
So what are we talking about
today?
This is about
how to put together
a nontrivial GWT application.
Something that you
actually want to ship.
Something that
you're going to have
more than two or three people
at a time working on
without stomping
on each other's toads.
Toes.
We're going to look,
in particular,
at the client side,
because that's where I tend
to pay attention.
And what we're really
exploring here
are lessons that were learned
over the past year
building the new AdWords
user interface.
AdWords is a small product
at Google.
You may have heard of it.
And it's recently been--
the user interface of that
has been revamped
from the ground up
as a GWT application.
But first, let's go
to the last slide
of the presentation.
If you take nothing else
away today,
there are three things that you
really want to remember.
First thing, which I'm not going
to talk about much at all,
but still,
you want to remember it,
get your browser history right,
and get it right early.
One of the first things
that you should nail down
on your app
if you want it to work
is what the back button
and the forth button
are going to do to your users,
how they're going
to be bookmarking things.
GWT gives you a very nice
history library
to take care of that,
and if you apply it early,
it's easy.
If you apply it
after the fact,
and try to retrofit it in,
not so easy.
The next takeaway point
is you're going to use
an Event Bus as the backbone
of your application
to let yourself--
let you make sure
that the left hand of your app
has no more
than a nodding acquaintance
with the right hand
of your app.
Preferably
through conversations
brokered by
a neutral third party.
And then finally, you're going
to start saying to yourself,
"DI + MVP FTW."
Which is to say,
dependency injection
and the Model/View/Presenter
pattern for the win.
And I promise that that'll all
make a lot more sense
in about 50 minutes or so.
So let us start
with a demonstration
of the app that I mentioned,
the AdWords UI.
Which I, of course,
forgot to cue up.
So give me a moment
to rectify that situation.
Yeah.
We might just blow off
this part of it
if I can't
get my act together here.
There it is.
And it has decided to reload.
Good.
So this is live right now.
I'm looking
at the production site.
If I run into glitches here,
there are people
in ten countries
around the world,
a number of users--
which number I have been
cautioned not to disclose--
but they'd notice.
You can see that this is
a pretty traditional kind of
table full of data
sort of app.
But we've got
nice little widgets
like the GWT Incubator
tree view, fast tree view,
helping us guide our way
through things here.
Our users are going to do things
using the paging scrolling table
from the GWT Incubator to make
lots of edits of their data.
Maybe several fields at once
in that kind of a thing.
Or I might want to type around
and change my matching
and change my CPC.
I forget what that means,
actually,
although this
is my live account.
So I'm not going to actually
commit any of these changes
that I'm making.
They might take advantage
of the GWT visualization stuff
that you might have
learned about yesterday
to show graphs and such.
There's my pretty graph.
I'm not doing so well, really.
Maybe we're going to compare
my click-through rate
to the number of clicks
that I get
and see if there's
a correlation there.
Yep, there is.
So this is the app
that we've built
with a team of more
than a few engineers.
I really forget the handful,
but the sizeable handful
has grown lately.
And we're all able
to commit changes to this
and test portions of this
without necessarily talking
to each other every day
and destroying
each other's code.
So let's get back
to the presentation.
And before we get back
to the presentation,
let's get my speaker notes back,
or this is going to be
a much shorter presentation.
Dude, it all worked.
All right.
So we've got three major themes
that we're going
to talk about here
on your way to making
no bad apps the AdWords way.
You're going to embrace
the asynchronous nature
of your app.
You are an AJAX developer.
And the first word in AJAX
is "asynchronous."
Let's just not kid ourselves.
You want to always be
decoupling your app.
Like I said, the bit over here
and the bit over here
just shouldn't really know
much about each other.
And finally,
you're going to strive
to achieve statelessness,
so as various bits of your
infrastructure fall over,
the other bits
aren't bothered by it.
So, embracing asynchrony.
These Keynote builds, man.
I can't get enough of 'em.
Yes.
The "A" in AJAX
is for asynchronous.
There is nothing interesting
that you're going to do
in your application
that isn't going to,
at some point or another,
require some kind
of an asynchronous callback.
So you need to just
come to terms with that fact.
You don't want to spend
a lot of energy
trying to create
little synchronous bits
that you can call
once everything is swapped in,
because you've got better things
to do with your time.
And if you do it well,
it's really not
such a bad thing.
So here's an example
of what you might start out
trying to do.
Through the talk,
I'm going to assume
that we're writing
an address book application.
A contact manager.
Could you all turn your cell
phone ringers on, please?
The first thing you're going
to do as a good developer
is you're going to create
your data objects,
your model objects
at the heart of your app.
And at the heart
of an address book
we have contacts.
And my contacts have things
like a name field,
and they have ContactDetails.
ContactDetails
are an interface
to cover phone numbers
and street addresses
and mother-in-law's birth date
and that sort of a thing.
And the mistake
that we made here
is that we are downloading
the kilobytes and kilobytes
of ContactDetail information
at the same time
that we're downloading
our contact objects.
At least,
that's what I see implied here.
That's a mistake.
The odds are that you want
to get your hands
on that list of contacts
with as little information
as you need as possible
to display them to your users,
and then you want
to move on from there
to fetch just the details
that the user cares about.
So probably you want a structure
that looks more like this.
My contact comes down
with a list of DetailIds.
Some kind of opaque Id object.
It's probably
a string under the hood.
But we're all
Java developers here.
We like our strong types.
And that way
we don't accidentally
ask for a contact
with a phone number Id.
Another small change
I made here,
which is kind of sneaky,
is that instead of using
a list of ContactDetails,
doing the Josh Bloch thing
and correctly providing
an interface
for my field name
rather than the implementation
class name.
I'm actually being very
straightforward about the fact
that this is an ArrayList
that I'm looking at.
I picked up this superstition
from a talk
that I may have misunderstood
on optimizing GWT RPC.
Where if you know that all
you're ever going to send
across the wire
are array list instances,
you don't want to give
the GWT RPC--
the magic generator--
a chance to try to pick up
every other possible
implementation of the list
and accidentally compile that
into your app.
Small point.
So here we are, writing
everything asynchronously.
And thinking to ourselves,
"This is really going to suck."
It doesn't have to.
If we wrap
our asynchronous calls
and asynchronous operations
in the command pattern,
life can be pretty good.
A command pattern
is a kind of a thing you do
where instead of having
a call for this--
you know, a "do this" method
and a "do that" method
and an "update the other"
method--
we have objects
that embody such calls.
So when it's time for me
to get ContactDetails,
I'll make an instance
of getContacts command,
and I'll hand that off
to a command processor
and receive back in return
a "got the contacts" response.
Every action I want to take
with my app,
I encapsulate into an object
like that.
And because I have all of these
nice little command objects,
I've got a nice unit
of currency
for implementing caching
policies.
Maybe whenever I see
the same GET request twice,
I'll cache away
the response I got last time
and just return that to myself
immediately.
Not bother
with a server-side trip.
I've got a place where I can do
batching mechanisms.
If I see five getCalls
in succession
that aren't interlayered
with updateCalls,
I'll have a timer going
where I can collect
all of those guys
and put them into one do this
group of operations command,
send that over
to the wire,
over the wire
in a single HTTP request,
rather than sending them off
as bits of the app ask for them.
I got a place that I can
centralize my failure handling.
So that every time I'm making
one of these async calls,
I can inherit the "oops,
something went wrong" behavior
rather than having
to call it out in line there.
This also, if I want to get
a little bit forward-looking
about this,
since I'm being honest
with myself
about the asynchronous nature
of what's going on here,
I've done all of the hard parts
of chopping up my app
into bits
that can be code split
at GWT.runAsync points.
I won't go into detail
on GWT.runAsync,
because I'm sure you've been
hearing about it for two days,
but do it this way
and you're halfway there.
If you ever read a book
on "how I'm going to implement
undo and redo
on my application," the odds are
the term "command pattern"
is going to come up.
And you're already working
that way.
Because you're so clever,
you're halfway there.
And then finally,
the work that you've done
to isolate yourself
from all of this RPC.
You've got a nice funnel point
at which you could slip in
your HTML5 persistence
type of code
when you decide you want to get
into offline work.
So...
And it shines your shoes too.
So let's look
at some actual code here,
how we might implement
something like this.
Are there any AdWords engineers
in the audience?
I can say anything.
Okay.
[audience laughs]
They won't actually recognize
this interface.
It's not exactly
what they do.
But there are tools
that they created
specifically for the project,
which when you pull back
the hood,
this is what's going on there.
So I assume most of you
are GWT developers
or you would be off
looking at Wave.
You probably recognize
the bottom half of this slide
where we are defining
a GWT RPC service.
When we're working
in this style,
we have a single service,
and that service provides
a single call,
which is basically
that "execute" word there.
Do something for me, and then
return to me the response.
What happened?
And then the various somethings
that we want our app to do
are going to implement
this Action interface,
which we couldn't call Command
because that word is taken
by another GWT class.
So an example
of an Action object
that we might write
would be this GetDetails thing.
Remember that we are working
on our address book,
because the world doesn't have
enough address books.
The command will be something
as simple as
I'll instantiate the thing.
I'll give it a list of the Ids
of the details that I want.
On the server,
I'll do something clever
to actually fulfill
that request
and send back across the wire
a GetDetailsResponse object,
which has this nice ArrayList
of the details
that I can then
go and render.
Now, I'm not trying to say
what the granularity
of your operation should be
when you write your app.
I'm implying
a pretty verbose world here
where I've got 500 different
flavors of Get command,
based on my 500 different
model objects.
Maybe you want to do something
a little bit more generic
and just have a single
Get something command
that's parameterized based on
the type that you're fetching.
Maybe it's going to make sense
for your app
to be more coarse-grained.
Maybe you want
to write actions like,
"Get me all the stuff I need
to render
the first page of the UI."
The level at which you break
stuff down is up to you.
I'm just trying
to pound home the idea
that you've got objects
that embody
what it is
you're trying to ask for
and the ways you ask for it.
And I took that digression
one slide too early.
We've had our...whoops.
We have our action class.
We have our response class.
To make the code a little bit
more pleasant to deal with,
we're going to write
probably a third class
which is a custom subclass
of the asynchronous callback
that happens--is handed
to each GWT RPC call.
This gives us a spot
to write failure handling
that the application code
can inherit
and not have to worry
its pretty little head about.
Maybe we'll pre-chew
the response a little bit
to keep everybody
from having to write
the Get, the Get, the Get,
the code
to actually get the meat
of the response.
So what I actually will end up
writing in my app
when it's time
to render a contact
or something like that
is a method like
this showContact thing.
Where I somehow magically--
we'll discuss it--
got my hands on the service.
I've called this execute method.
I've given it an instance
of the GetDetails command
that I want it to run.
And I've instantiated
my callback GotDetails
so that once I've got
all my information,
I can call renderContact,
I can call renderDetails,
and go mess with widgets
and stuff.
As Java goes,
that's fairly concise.
We don't have anonymous...
we don't have closures
and probably never will.
But this is about as short
as it's going to get
in a Java sort of world.
So, on with the theming.
I mentioned that you want
to decouple your app.
The whole left hand/right hand
don't talk situation.
The way that we're going
to make that happen
is the combination
of an Event Bus,
using the MVP pattern
when we have custom widgets
to implement,
and by using
dependency injection,
preferably via a tool
like Google GIN,
which is derived
from Google Guice,
which all of you--
all of you
should be attending
the Guice tech talks
that follow this one,
by the way,
since you're already
blowing off the Wave guys.
Guice, Guice, Guice.
Let's go into depth
on each of those.
Oh, the reason
that you're going to do this
is because it buys you things.
Because your app
is nicely encapsulated,
you're going to be able
to throw away bits of it
without the entire thing
falling over.
You're going to give yourself
a spot
where you can delay,
as long as possible,
the slow DOM operations
that you might want to be--
required to perform.
Your application logic
will be separated enough
from your widget
and DOM manipulation logic
that the slow things can happen
later or never.
You're going to find
that unit testing
is a lot simpler to do.
If I can--
if it's very clear
how I instantiate
this particular part of the app,
it's going to be easy for me
to test
that particular part of the app
without having to swap in
the other 500 parts of the app.
And if I do this right,
I'm going to have
really fast unit tests
that I can execute in a JRE
rather than in a GWTTestCase.
So unit tests that execute
in microseconds
rather than in
the 20-whatever seconds
it takes to launch GWTTestCase
these days.
Which time we are improving,
let me say.
But it'll never be the same
as simply not doing it at all.
So what's an Event Bus?
Let's show you first
what an Event Bus is not.
You're writing your app
in a typical style,
and you've probably
created yourself
a bunch of model objects
in our address book
that are probably contact groups
and contacts.
And they got property lists.
They're throwing
property change events.
Each one has
a list of listeners
that are watching for changes
to the thing.
And we'll start adding in
bits of our UI.
We've got our contact
group list UI,
which is going to have
a listener attached
to each one
of these group objects.
And then we've got
the list of contacts
within an individual group
that's listening,
attaching handlers--
event handlers
to some of those contact guys.
We probably have a display
with individual contacts
that the list UI needs to know
how to swap in.
And the contact list is going
to want to edit these things,
and these are all going to be
listening to each other
for update notifications,
you know,
show yourself, hide yourself,
I'm done now.
And then marketing comes in
and says,
"We don't have enough
bread crumbs on this thing,
and that Window Title's
looking kind of sloppy."
We got kind of
a messy world here.
We've got a lot of handler
objects that we've instantiated.
We've got a lot of things
that we can forget to decouple
when it's time
to tear things down.
It's not a fun world.
I won't even talk
about testing.
It's an excellent profile.
So we can reduce this coupling
in a couple of steps.
But first, we'll introduce
our Event Bus.
This thing
on the side of the app
that everybody has access to,
where they can listen
for the stuff that has happened
in the application
that might be
of interest to them
without necessarily
needing to know
who was responsible for that
event in the first place.
So, for example,
when the contact list UI
wants to tell the world,
"Hey, the user thinks that
it's time to look at the details
of contact X,"
it can just post that event
to the Event Bus.
And whatever parts of the UI
are responsible
for actually showing
the user contact display,
contact number X
can take care of doing that.
And it doesn't have to know
that it's--this week,
it's the contact list UI
that's responsible
for doing that,
even though next week
it'll probably be
some other gesture
that some UI designer--
respected UI-designing
colleague--
came up with.
But you see we still have
a lot of mess
in the middle there.
We've got all these
property change listeners
that I keep clearly implying
I don't think
you should have.
And what I espouse
doing instead
is just getting rid
of that model core.
Not saying that you don't have
model objects,
just saying that they're not
terribly of central importance.
Instead, remember that we know
that everything that we do
is asynchronous.
Let's just push
that asynchrony
right through to the bottom
of our application.
When the contact display UI
is ready to display somebody,
it'll tell the RPC service,
"Hey, get me contact XYZ.
I've been told to display him."
With any luck,
that's cached,
and so my callback
fires right away,
and I've got him immediately.
When the editing UI needs
to mess with that contact,
and change his data,
he's going
to tell the RPC service,
"Hey, I've got an update
for you.
Push that down to the service
for me."
When the response comes back in
to the RPC service,
he will announce to everybody
who is interested,
"A contact has changed.
"Here is the new copy
of that contact.
Why don't you all who are
interested display that thing?"
And everything is nicely
isolated from each other.
So let's look at what the code
would shape up as for that.
You'll remember
this showContact method
that we wrote earlier.
We're going to add
a little bit to it.
We're going to start
paying attention
to just which contact
we're looking at at the moment
over here
in our contact display UI.
So just something as simple
as holding on to that Id object
so that we know
who's important to us.
Before we ever tried
to show a contact,
we attached ourselves
to the listener bus.
Sorry, the Event Bus.
We're going to use a GWT class
to actually instantiate
our Event Bus,
because that's simpler
than writing our own.
With GWT 1.6, we revamped
our event handling mechanism.
And at the heart
of each one of our widgets
is a handy class
called the HandlerManager.
There's no reason
that you can't use
your own instance
of HandlerManager
to provide your app-wide event
dispatch services.
So our contact display
has somehow magically
gotten its hand
on the HandlerManager
Event Bus instance
for this app.
And he's attaching to it
a typical event handler.
You've created for yourself
a custom ChangeEvent type
called ContactChangeEvent.
And you're implementing
your custom event handling
interface.
So when a contact change
comes in,
you're going to look
at the new contact object
that's hanging off
of that event,
see if it's the one
that you're actually displaying,
and if so,
do something about it.
And a subtlety here
is that you don't really know
if that's the one and only
copy of this contact,
if it's one
that's been handed to you
for your purposes
at this moment.
The contact goes from being
of central importance
where you might be attaching
handlers to and from it
to this kind of ephemeral,
disposable thing
you can kind of wad up
and toss
on the floor when you're done.
But don't change it,
'cause other people
might be looking.
Continuing with the code,
let's move over
to the RPC service itself.
What we're doing here is we have
wrapped the GWT RPC service
that we defined
with somebody who's implementing
the interface itself.
In this particular case,
we're taking that execute method
that our RPC service provides
and we're overloading it
to provide specialized handling
of this UpdateContact
type of action.
And the interesting bit is that
we are wrapping the callback
that the caller gave us
with our own callback,
which on a successful response
is going to update
whatever kind of a cache
we're maintaining.
It'll fire off
the real onSuccess result
for the real callback,
and then it's going to tell
the rest of the application,
"Hey, contact changed.
You might want to do something
about that."
It's as simple as that.
So continuing
with our decoupling theme.
I keep saying
"MVP, MVP, MVP."
And I did not say MVC.
Now the odds are
that when you got out
of the five or ten years
you spent doing JSP
and Web 1.0 app,
you kind of dusted off
some desktop knowledge
or somebody who had once
done that kind of work
told you, "You know
what we should really be doing
"now that we've got
these widgets
is model, view, controller."
And it's this kind
of triangular thing.
And the model throws events.
And then the view throws events.
And the controller
messes with both of them.
And I forget what code
goes in the controller
and what code
goes into the view.
And no two people
will give you the same answer
for what MVC actually means.
There are a handful of guys
who wrote Smalltalk-80,
but they may well be retired.
You also wind up
with a problem here
that, like I said,
some of your code
landed in the controller.
Some of it landed
in the view.
You need to test it all.
And the code that landed
in the view
is not going to run
real fast.
It's messing with the DOM.
And you either need to have
a pretend DOM there
if you're lucky,
which we don't provide you--
yet--
or you need to fire up
a real DOM.
And those tests are just not
going to run fast.
So what you want to do instead
is use the model, view,
presenter pattern.
We found ourselves implementing
this style kind of by accident,
just trying to isolate our code
from tests.
And then some people
who are brighter than me
were reading
Martin Fowler's blog
and saw that he had already
invented this pattern
before most of us
were professionally active
and had called it MVP.
So we call it that too.
Then what you're doing
is just much simpler layering.
Your view class
down there at the bottom
is the only thing
that actually knows anything
about DOM elements
and widgets.
And it's a source of things
like KeyboardEvent
and ClickEvent and very
browser-specific events.
The model at the top,
we're familiar with
at this point.
It's this kind of
overglorified bag
of setters and getters
that we're using
because we decided
we don't like
property change listeners.
And that leaves the presenter
in the middle
as the only object
that has anything
that's really worth testing.
This is the thing
that knows how to take, say,
a contact in one end
and take its values
and cram them into
a bunch of text fields
on the other side,
listen for changes
to those text fields,
and interpret them
as something--
as values to push back
into the model.
When it comes test time,
we can ignore
that silly little collection
of DOM widgets that we created
and replace it
with a bunch of mocks.
And in fact, we can even use
the GWT MockUtilities package,
which I think was also
introduced in 1.6,
to use something
like EasyMock
or JMock--
or actually,
there's something called
Mockalicious now,
which looks interesting--
to provide fake test fields,
fake this and fake the other,
and run them under a JRE.
Although today I'm actually not
going to show you doing that,
because I found that for
the pretend code
that I'm putting together,
it was easier
to do the mocks by hand.
So let's get concrete again.
In our address book,
we are doing things
like editing contact details.
Say we're going to edit
somebody's phone number.
So a phone number
looks like that,
it's got an Id,
it's got the telephone number,
and it's got the work, home,
mobile, babysitter label.
The UI probably looks
something like this.
I'm sure we've all
seen that numerous times.
And all
of the interesting stuff
is here
at the presenter layer,
where we're going
to implement
a thing called
the PhoneEditor,
whose job in life
is to take a phone object
in one end
and a phone UI in the other end
and make them all go.
So the PhoneEditor probably
looks something like this.
At least it does
when I write it.
Oh, the brace
is in the wrong place.
Damn.
It's going to define
an interface
for the kinds of widgets
it wishes it was talking to.
And you'll note
that I'm describing things
like a save button
and a cancel button
and a number field.
But I'm not calling them
"button."
I'm calling them
HasClickHandlers
and HasValue<String>.
These are characteristic
interfaces
that the GWT widget set
is littered with.
And if I define
my display interface
in terms of these
characteristic interfaces,
it becomes quite easy
at test time
for me to provide
mock instances of those things.
A lot of people think that
providing this display interface
is more trouble
than it's worth.
And they will tend to give
their presenter objects
actual knowledge
of the specific widgets,
like text field,
like button.
Not a big deal,
because you can, again,
use EasyMock or its like
to put in fake versions
of those in the JRE.
But that made the slides
too big,
so I didn't do that today.
So we've got
our display interface.
How are we actually
going to use the thing?
At some point in the lifetime
of our PhoneEditor,
we'll be handed
our display object,
which--don't tell anybody--
it's a composite.
We'll hold on to that thing.
We'll attach a few handlers
to it.
OnClick of the save button,
I want to call my doSave method.
OnClick of my cancel button,
I'm gonna do my cancel method.
Sooner or later,
somebody's going to tell me
to edit a phone number.
And I'll do that simply
by holding on
to the phone instance
that I'm editing.
I'm implying a kind of
sneaky thing there
with that static method
on phone.
I'm figuring that
the instance of phone
that I'm editing
may not turn out to be
the one that I actually want
to keep.
And to make sure that I'm not
going to accidentally
mess with the phone object
that other parts of the app
might be trying to display,
I'm going to assume
that I defined
a kind of a clone style method
on my POJO.
You can follow this convention
or make up something else.
This is more or less
what we did,
except our code was uglier.
Once I've got this safe copy
of the phone,
which I can toss away
when I'm done
because model objects
aren't special anymore,
I'll just tell my display
to show the number
and update the label picker
and that kind of thing.
Oh, there's another cheat
that I'm doing in here.
I have implied--
head back
to our interface here--
I have implied that GWT has
a pop-up widget
that implements
the HasValue interface.
It does not.
We ran out of time.
I think it will have such
sooner or later.
Maybe even in the 2.0 timeframe.
I need to talk to my boss
about that.
But in the meantime,
it is no big thing
for you to write
your own HasValue wrapper
around the list picker
as you're going to use it.
We did.
And it didn't take us
hardly any time at all.
So full disclosure
out of the way.
Now let's actually save
what the user has done.
Again, it's a simple thing
to pull the values
back out of
that display instance
and then fire off my nice
asynchronous service method.
When I hear back
that my POJO--
my phone number
was updated successfully,
I'll tear myself down,
whatever teardown means
for my PhoneEditor.
If there were errors,
I'll have some error reporting
and rendering convention
that I will have implemented,
and I'll display
those things there.
Obviously,
I'm glossing over a lot.
And you'll call me on that
at question time.
But until then,
I can just get away with this.
So let's talk about
dependency injection.
I'm sure you've all heard
the term.
And you've all been meaning
to adopt it,
and it seems like a good idea,
but maybe
on my next project.
Dependency injection
ain't no thing.
It is just a pattern.
All it means is that instead
of having global instances
of my services
that I reach out for,
like GetEventBus.singleton
instance,
GetRPC.singleton instance.
And instead of using
a service locator
that I kind of cram full
of every interesting
property of my app
and shove it down the throat
of any part of the app
that might want it,
I'm very explicit,
preferably via constructor
arguments,
about just what it is
this particular object needs
to exist in a useful way.
DI is really not at all hard
to do manually.
And in fact, AdWords does do
all of its DI manually,
because we didn't have any way
to do it automatically
at the time
that we started the project.
In the interim,
there was a GWT developer
on another project...
this is public.
You know the contact manager
in the personal profiles
that Google is starting
to flog these days?
The group that put that together
were a big fan
of the Guice dependency
injection framework for Java,
and they wrote
their own version of it,
called GIN.
And now more and more apps,
internally and publicly,
are taking advantage of that.
You can find both Guice
and GIN
at the URLs provided there.
There are also talks coming up
on Guice right after this one,
and if you leave this room,
I suggest you go to that room.
I'm not sure which one it is,
but it's on
your little badge-y thing.
I had a picture
of Snoop Dogg here
for the GIN and Guice thing,
but the lawyers
wouldn't let me do it.
So, sorry.
So what does the code
actually look like
when we do something like this?
Well, let's start
at the bottom of it.
Let's assume
that we have a widget
that is a widget
in the MVP style
that is my contactViewer.
And that's kind of
the heart of my app.
And when the contact viewer
is ready to go,
it wants to get the root panel
and just start
to jump into it.
So what do I need to do
to instantiate
my contactViewer?
Well, I'm going to look
at its constructor arguments.
It needs its ContactWidget,
probably implementing
its display interface,
because I wrote it.
It's going to need an instance
of that PhoneEditor
MVP presenter thingy
because it's going to--
it uses that end line.
It doesn't go talk to it
through the Event Bus.
Not everything needs to talk
to everything else
through the Event Bus.
Things that want to deal
with each other directly--
there's no reason
that they can't have
handles onto each other.
We still like composition
in this world.
It needs the RPC service
to ask for the contacts,
and it needs the Event Bus
to listen for changes
to the contacts
that it cares about.
So it's going to get its hand
on the ContactWidget
with a simple "new."
It's going to get
that PhoneEditor
with a simple "new"--ooh.
But the PhoneEditor
has dependencies too.
It's going to need a widget
and it too needs its hands
on the RPC service.
But we're not really using
the actual GWT generated
RPC service.
We have our wrapping thing
around that.
So let's instantiate
our CachedBatching rpcService,
into which we will jam
the one that we GWT.created
to do the real work.
And then finally,
everybody seems to want
this Event Bus thing,
so we'll instantiate
one of those.
So that's kind of a lot of code,
but it wasn't really
hard to look at.
Part of the payoff
starts to come
when new dependencies appear.
Marketing has heard about
this Google Voice service,
and they're
absolutely convinced
that we should have
Google Voice integration
with the PhoneEditor.
So we're going to add that
by instantiating
the voice service.
We're going to change
the PhoneEditor constructor.
And nobody else
knows about that change.
Even though the PhoneEditor
is fairly far down
in our tiny little stack,
the thing that needs
a PhoneEditor
because it never
instantiated it itself
doesn't need to know
that PhoneEditor
now needs the VoiceService.
And I didn't have to add
a VoiceService argument
and a VoiceService argument
and a VoiceService argument
at every level in my code.
But still, you can see
that this is going to grow
into a lot of kind of
grungy code to maintain,
and a big list
of instantiation
to do all at the top.
If you were to use
a framework to do it,
something like GIN--
talk--
Actually, there's no talk
about GIN itself today.
I don't want to imply that.
But there is a talk about Guice.
And GIN is just like Guice,
but on the client side.
Anyway,
if you use GIN correctly,
and using it correctly
is easier
than using it incorrectly,
the code winds up
looking like this.
So I highly recommend it.
Now why are we doing
all of this stuff?
What's the punch line?
Besides having the app
isolated from itself
for that purposes,
we're particularly happy
because this gives us
easy to write
and fast tests.
So to write a test
in this style,
we need our mock objects.
We're not using EasyMock here,
so we're just implementing
these things ourselves.
We're going to make stuff
that looks like this.
I'm going to have
a mock version
of my ContactsService.
And it's going to have
these fields here,
the lastAction
and the lastCallback,
and I'm going to fill those
at obvious times.
When somebody gives me a pretend
execute this object,
execute this action call,
I'll hold on to it
so that my test code
can take a peek.
Throughout
the code examples here,
I've been leaving off things
like public and private
and so forth so on.
On this slide,
that's not pretend.
I probably would have
package access
or even public access
to the fields
on this mock object
because there's not
a lot to be gained.
By having accessor discipline,
I wind up
with more concise code.
We're going to need
a few more mock objects
as we're working through here.
So I'm going to have to have
a nice fake version
of that ClickEvent.
Because for reasons
that I don't remember,
we decided you shouldn't be able
to do new ClickEvent.
But let's not worry
about that.
We have a mock version
of the thing
that has ClickHandlers.
This is our pretend button.
It's got the same notion
of the last ClickHandler
that was added to me.
I'll hold on to it
so that people can find it.
When I'm told to...
Does this do anything else
interesting?
Nope.
Yeah, in fact, the fireEvent,
it doesn't even do anything.
It's a no-op.
Our tests don't tend to do that.
Similarly, we had
our HasValue thing.
We've got a mock version
of that
that'll hold on
to the last value
that was set into it.
It also,
because I have access to...
Never mind.
That's not a point
worth making.
And then finally,
we'll put together
a bigger, more interesting
composite mock object,
the thing that implements
our PhoneEditorDisplay.
So where we would have needed
a phone editing widget,
remember that our PhoneEditor
presenter
wants a phone edit widget.
We gave it a nice instance
of MockPhoneEditorDisplay,
which is made up of
MockHasClickHandlers
and some MockHasValues.
Oops.
Oh, well.
Some fake accessor methods
for those,
and again,
nice visible fields
so that the test
can play with it.
So that's a lot of work
that we had to do
for what's going to be a test
for three lines of code.
On the other hand,
the three lines of code
will probably be
more interesting in real life
when I need to test, say,
"Did I warn the user
that they're about to abandon
their changes when they navigate
away from this page?"
And when I have tests
for the cancel path
and for the save path,
all of these guys
are eminently reusable.
In fact,
you might be asking yourself,
"Why doesn't the GWT team
provide me
"with a prerolled
MockHasValues
and MockClickHandlers?"
And yeah,
we should probably do that.
So on to the test itself.
We're going to put together
our mock world.
We've got our fake service.
We've got our fake display.
We're creating
our fake phone object
that we wanted to be editing.
And we're setting--actually,
it's a real phone object
that we wanted to be editing.
And we're setting up
another one
for how we expect
the "after" phone object
to look that we're going
to send across the wire
for comparison purposes.
We're going to instantiate
the PhoneEditor,
the thing that we actually
want to test here.
We're going to tell it
"Go edit this phone number,"
just like we would tell it
in our real code.
And then we're going to sneak
into our mock objects.
We're going to tell
the labelPicker,
"Okay, the user typed 'work.'"
And we're going to tell
our number field,
"This is the phone number
she provided."
And then we're going to tell
the fake save button,
"Do a click for me now, okay?"
That should have been enough
to kick off
our presenter object,
the PhoneEditor.
And now we're going
to verify all of that.
We're going to sneak
into our service.
We're going to see what action
it actually requested.
We're going to make sure
that it was an instance
of UpdatePhone,
based on this expected...
Oh.
That'll work, actually.
It should've been
UpdatePhone before,
but they're equivalent
because they have
the same contactId
is the idea.
And I did run this test.
It compiles and passes.
Buh-buh-buh-buh-buh.
And then we get out
of the action.
The phone object that was
actually sent across the wire,
we compare
the interesting bits of that
with what we expected to see,
and our test works.
So, um...
oh, blew the transition.
There we go.
So finally, I talked about
you wanting to have
statelessness going on
in your app.
And what
I'm talking about there
is you want to have
a lot of disposability.
When your server falls over,
your user doesn't want
a notice about it.
What I'm saying here is,
you really do want
to have stateless servers.
And in particular,
the--doing anything interesting
based on the session,
the Servlet session object
on your server,
is just a bad idea.
It's a fine thing
to use for caching.
Caching is a wonderful thing.
But anything--
If you're coming
from a JSP background
and you've written
a multi-step wizard--
you know, step one,
step two, step three--
there's a good chance
that you implemented that
via some kind of session object
on your server.
And your user's sort of
halfway through your wizard
when the server falls over
or its refresh time--
they're all screwed.
Or you wrote something
really confusing
and difficult to maintain
to try to bounce
that session state
from one server to another,
and that was more trouble
than it was worth
and pagers go off and stuff.
Don't do that anymore.
You've got a browser now.
You're an AJAX developer.
The browser provides
all the server state,
all the server session
you actually need.
So that means that when
it comes time
for rolling updates
and servers are falling over
and new ones
are taking their place,
or it means that at your desk,
when you need to restart
your server
because you rejiggered
some of the business logic code,
what's going on
in your browser
doesn't even need to notice
that that happened.
Maybe some calls will be
a little bit slower
because the cache fell off.
But otherwise you're fine.
If you're developing
an App Engine app,
you've got the Memcache package
that's available for you
which works really, really well
for this kind of thing.
So we've got disposable servers.
We similarly want to have
disposable clients.
I'm afraid I'm going
to gloss over this
more than it deserves,
but the punch line here is,
as I said
at the beginning,
you want to get
your history right.
As your user
is doing things
that feel like navigating
around your app--
you remember
in the AdWords demo,
I was clicking
around the tree
and clicking on--
navigating
into different parts
of the app.
Each one of those places
in your app
wants to be embodied
by a GWT History token,
so that as the user
hits that back button,
she'll return to where she was.
If she hits the forth button,
she goes on.
If she keeps a bookmark,
that bookmark actually works,
and she returns to the space
where she was in the app.
If you've done that right,
you've got a disposable client.
When the user hits F5,
or you copy a URL
from one browser to another,
it just works.
Your development life
is happier.
Your users are happier.
That said,
the History library
is in the traditional GWT way.
It is a bit low-level.
Making every bit of your app
knowing about History tokens,
and knowing to listen
for OnHistory token change,
and to try to push
History tokens
if they want to change
is sort of nasty.
And you want to come up
with some kind of an abstraction
to isolate
the bits of your app
from that kind of knowledge.
What we did in AdWords
was we came up
with a package called Place.
It's not open sourced.
It wound up being
very ad-specific.
But the pattern itself
is pretty straightforward.
It pretty much winds up being,
just like I have
command objects, action objects
to embody operations,
I've got place objects to embody
addresses within my app.
They're kind of
incarnate bookmarks
and each place object
knows how to talk
to the History service,
to push tokens,
and read tokens
and that kind of a thing.
And it's all integrated
into the Event Bus.
So you remember this picture,
where all of my bits of my app
are talking to each other.
To introduce place management
into this,
I'll create something
called the place service,
which is the only part
of the app
that's actually responsible
for knowing about
the GWT History library.
So when a bookmark
is pasted in,
when the back button is hit,
when the forth button is hit,
place service
notices about that.
And it announces
to the rest of the app,
"The user has changed places.
Here's the new place
that they're going."
Maybe it represents
a contact.
Maybe it represents a text ad
in AdWords.
And the bits of the app
that need to swap themselves in
or swap themselves out
can listen
for those more semantic objects,
the place changes,
and go on their merry way.
I've actually run out of things
to talk about.
Like I said, it always
ran long before.
So I did talk faster.
But we have ample time
for questions and answers now.
So if people could line up
at the microphones,
so I don't have to try
to repeat your question
and get it wrong,
this would be
an excellent time.
[applause]
Thank you.
man: I just wanted
to reiterate
something you mentioned briefly,
that...
about creating
some of these mock objects
inside of GWT.
And not so much a question,
but a hope
that that is something
that would happen,
because anything we can do
to make it easier
for testability
and to get these designs right,
I think that would really help
with adoption.
Because in past projects,
we've tried to use it,
and it's really difficult
to get it right
and to get the decoupling in.
So if that was baked in
to just standard GWT,
that would be, I think,
a really, really
beneficial adoption part.
Ryan: We totally agree.
And we're getting better--
learning more
about that kind of thing
as real people
build real applications
inside of Google
and yell and scream at us.
Before we get to actual changes
inside of GWT,
one of the things that I'd hoped
to deliver today
was a simple sample app
that actually embodies
all of these practices.
So if nothing else,
there's code to point at
and show people,
"No, do it that way."
And I'm going to try
to block out some time
to actually finish that thing
and post it
up on the GWT website
sooner or later.
man: First I just wanted
to say thanks.
All the things in this talk
were definitely things
I've been struggling with.
Ryan: Great.
man: I was about halfway there,
and this kind of--
going to help me fix things.
I was just going to ask
about testing and mocking.
I've seen a lot of discussions
back and forth
about is it feasible
to test the GUI itself.
So the techniques here
are great.
I didn't think about doing it
where you're actually
getting most of your code
out of the DOM
and you're just testing
everything else.
But yet, let's say
you're trying to make
a very complicated,
you know, interface.
Drag and drop, resizing,
crazy stuff.
Ryan: Yep.
man: How do you test that?
Ryan: Then you're back
to a GWTTestCase.
But, you know, it's not like
GWTTestCase is poison.
You've got a--kind of
a 20-second startup time
to get it going,
but each individual test
beyond that actually
runs pretty quickly.
And as long as you've got...
We've got a discipline
within Google
about small, medium,
and large tests.
And I think for a GWT app,
that breaks down
into JRE-type unit tests
are the small tests.
You want those to be
about 70% of your test coverage
in an ideal world.
And GWTTestCases
are medium tests.
The ones that, you know,
they're a little bit
more complicated,
but they're still not
involving
setting up the entire world.
If I'm writing a widget
that has enough DOM-based code
that it'd be worth testing,
I'll write a sample app.
Or just a display of that.
And I'll have a GWTTestCase
that drives that thing.
And then the third part
of that puzzle,
the large tests,
for us, that tends to mean
Selenium tests,
where I'm actually going
to put my entire app together--
or maybe put together some
subpart of my app together
if my app is too big
to want to fire it up
every time I test
on everything--
and have kind of
happy path tests
that make sure that I can flow
through the entire app
and off to other pages
and back again.
That sort of thing.
There's also work being done
under the Selenium umbrella
called WebDriver--
which I think is going to be
rechristened Selenium 2.0--
that provides
much better browser control.
Actual little plug-ins
that can go inside of IE
in particular.
So that instead of having
to pretend to do a click
and find that
you emulated it wrong,
it will do
an honest-to-God click
of an honest-to-God button.
You can test off of that.
So, 70-20-10.
Small, medium, and large.
man: Would that new
Selenium stuff
work in conjunction
with the in-browser hosting mode
where it's kind of
built in right now?
Ryan: Yeah.
man: 'Cause the stuff
that's in there now
is kind of--
there's docs on it,
but it's not really
well exposed.
Ryan: We're still getting
our story straight
on how to get those all
to play well together.
Another fun thing
that we started looking at
is the HtmlUnit package.
They've been, for quite
a while now, saying,
"Hey, guys.
We work with GWT apps."
And we're looking to actually
take advantage of that fact
in GWT for you.
So that when you do stuff
like run a GWTTestCase
that doesn't actually care
about accurate layout
or that kind of a thing,
you can talk
to the pretend browser
in this HtmlUnit package
and get faster execution
that way.
But still even with that,
the more you can isolate
application logic
from view logic,
the simpler and faster
your tests are going to be.
man: We're using the Event Bus
approach, partially.
I'm curious what you do
if a bunch of objects
that get put on it that aren't
being used anymore--
they're basically
dead listeners
that are added
but never removed.
And how do you manage that?
Ryan: Uh, badly.
We don't have weak references.
This isn't really Java.
It's just a pretend Java.
So you pretty much have to...
It's all developer discipline
of listening--well, actually,
the place change event
simplifies that some.
Because we've got that
at the heart of our app,
widgets are able to hear,
"I'm not relevant right now,
and so I'm going to--
"When a place change happens,
"and it's not a place
that I live in,
"I'm going to tear down
my Event Bus handlers
and throw those away."
We...of all of the many
fascinating performance
and memory problems
we have run into
in the course of AdWords,
I don't think that that one
has proven to be
too big of a deal.
I'd put that down to having
that kind of consistent
place handling story.
man: Okay, related to that,
if I have multiple tables
and I have an object
that's interested in events
from one of the tables
and you want to use
this Event Bus approach
to communicate that,
how do I know
which table it is
without being
directly connected to them?
Is there--have you guys
solved that problem?
Without knowing
the object itself.
Loosely coupling.
Ryan: Yeah.
I'm not following the question.
man: Maybe it's too complex.
Ryan: If you want to come up
afterward,
we can hash it out.
man: I just thought of--
the other question I had--
So I went through the whole
property change listeners mess.
And I'm looking forward
to getting rid of them.
But how do you deal
with efficiency?
Like, now you get an event
that this object changed.
And you have to update
the UI to reflect it.
But you don't know exactly--
if you knew, like,
this one field changed,
you can just go
and change one field
or move the corner over.
But now all you know
is something changed.
And how do you get around...
Ryan: Well, I know this...
I know the something
that it is
that is actually interesting
to me.
And you've got
more efficiency now,
because instead of having
this one-to-one mapping
of everything that wants
to listen to something
and what they're listening to,
I've got a lot fewer
instances of handler.
The execution speed,
I don't think,
has turned out to be
such a big deal.
Especially if I'm good
about only listening
while it makes sense
for me to listen.
So if I'm
the tree of contacts,
just, you know, then I'm
displaying all of them,
well, every single
contact event
is going to be
of interest to me.
If I'm the editing UI
that's only displaying
a particular contact
at the moment,
it's very unlikely
that some other contact changed,
because
I'm only one application.
I'm not, like,
some kind of server
handling all of the stuff.
man: And what if there is...
Our app has, a lot of times,
more than one view
on the same object
in different ways.
Ryan: But you're still--
you're usually not
going to have
hundreds of views, right?
There have--
two or three views,
and those two or three views
will be listening
and maybe will hear
some irrelevant stuff.
I think that that'll all
wash out.
man: All right.
Thanks.
Ryan: Hi.
man: Yeah, my question is,
you talked about
the importance
of being able to bookmark
or refresh pages.
So going with
the contact example,
you know, it's not just,
you know, good enough
that I'm on, you know,
hit the back button
and it knows I was at
the contact page.
You know, I want
to hit the back button and know
I was viewing, you know,
John Doe.
And then I want to hit
the back button again
and know, you know,
it was Jane Smith.
So, you know,
I'm kind of wondering,
like, in the HTML
or in the Web 1.0
or JSP world,
you would pass URL parameters.
You know, with
a question mark Id.
Ryan: That's pretty much how
the History library's
implemented.
Except everything winds up
after the hash mark
rather than after
the question mark.
So what you wind up doing
is I've got
a subclass of place
per type of object
that I want to be--
that my location
is relevant to.
So in the AdWords case,
we wind up with
campaign places,
ad group places,
and stuff like that.
These place objects
know enough
about the History
management system
to provide
a History token string
that includes the ad group Id
and the campaign Id.
And so boom,
it's right back there
in the URL.
man: And so then
does the place interact
with the Event Bus,
so when it notices...
Ryan: Yes.
man: That a new Id was passed,
it triggers an event?
Ryan: Exactly.
man: And notifies pages?
Ryan: So we can go back
to our pretty picture here.
That pretty picture.
So the user hits the back button
or the forth button.
The History event
hits the place service,
which is where the code
that actually implements
the History client interface
lives.
The place service instantiates
a new little place object,
which...let's see.
The place service might actually
talk to your RPC service
to, you know, say,
"Get me this contact
that is relevant
to the URL that I just had."
And then it will announce
to the world,
"Hi, everybody,
we're at a new place.
And this is the contact object
that I found at that place."
Nobody else has to know
about the fact
that it happened to live
on the URL
or whatever else went on.
man: And then, you know,
around your Event Bus,
how--
I'm just kind of interested
in how many different things
that subscribe
to a single event,
like, you know--
let's say I have
a tiered navigation,
you know, maybe.
Or I have top navigation
and side navigation.
So kind of pages
within pages with, you know--
or in terms of GWT,
like a panel within a panel.
You know, maybe I have,
you know,
a bunch of tabs at the top.
And I have a contact tab
and I have tabs on the left.
I mean, does--
do you try and just subscribe
to the event in one place
and then top down drive,
like, "Hey, this was changed,"
or do you have
multiple places--
Ryan: No, it...
People did start talking
directly to the Event Bus
all of the time
for everything.
And that was
kind of retrograde.
If I've just got--
It's like
in the PhoneEditor example.
The PhoneEditor...
Or in the dependency injection
example.
We created our contact display
and we gave it an instance
of PhoneEditor display.
PhoneEditor
didn't have to know
about navigation changes
within the app
because it had
a really tight relationship
with the contact display object
that needs to know
how to edit phone numbers.
There's no reason for that
to go crazy with it there.
So I don't have
a pat answer for you.
But I think it's the...
the top-level bits of the app
that need to appear
and disappear
are probably the ones
listening to, for example,
place changes.
Maybe other types of events--
you know, contact editing
or whatever, maybe there you do
want to be a bit finer grained.
But you kind of...
It becomes a feel thing.
I don't really have
a simple answer.
man: So have you guys
thought about
just taking a simple app
like this
and putting it out there
as an example?
Ryan: Yeah, absolutely.
man: 'Cause, I mean,
you know, what's out there,
I think it was the sandbox
or the--
Ryan: Yeah, um--
man: It's nice,
but it's not really
real-world scenarios.
Ryan: I have been
reliably informed
that I will be doing that.
man: Well, thanks.
Appreciate it.
[audience chuckles]
man: Hi.
Quick question.
I started recently using GWT,
and I really like it.
My question is more about
the History button in general,
since you've talked about
different design paradigms
and patterns.
Is History button really,
in your view,
still a useful thing?
Or not History button.
The back button,
I'm sorry, I meant.
Ryan: Oh, sure.
man: Because now we're talking
more and more
rich applications.
You know,
fancy applications.
More like
desktop applications.
You really--you know,
once you add--
you may want
to let them delete it,
but not necessarily delete
by going back, for example.
So--
Ryan: I wouldn't--
You don't want to let them
delete by going back.
But...especially if you're
doing an application like this,
where at the end of the day,
it's a big bag of objects
and I want to navigate
around the space
that that big bag of objects
defines,
your users are used
to clicking on the thing
and then
hitting the back button
to get back to the place
that they were before.
I'm certainly not suggesting
that you encourage--
that the back button becomes
your undo mechanism.
I think you'll go insane.
But you can't stop your user
from hitting that back button.
And it's a pretty nice way
to work your way
through a space.
man: What we did in our app
is basically
we would stop having
the back button at all.
So as a result,
user get used
to using in a different way.
More like they're used to using
a rich application.
Ryan: I've never had the luxury
of working in an app
where I was allowed
to take the back button away.
So...but I wouldn't want to.
I mean, it's...
There's a reason
that the Web browsers
got shaped the way they did--
it feels really good.
If you're--I mean--not every app
is this kind of an app.
I don't know if the back button
really makes sense for Lombardi,
where they're, you know,
doing more--
I'm doing
a big diagramming thing.
But for an app like this,
I think it's pretty natural.
And it's not something
to hide from.
man: Thank you.
man: When you build the UI
in GWT,
it's very developer-centric,
usually.
It's basically Swing.
It feels like that
when I'm building a UI.
But usually, UI designers
want to make it with mark-up.
So there was some talk
a while ago
about a declarative UI.
I may have missed it
if that came out.
But what do you use
for AdWords,
and are there--
Ryan: We use declarative UI.
man: Yeah?
Ryan: The code's there.
It exists.
It's called UiBinder now.
Its documentation
is all public,
to make it painfully obvious
that we haven't actually
released the code itself.
We are trying to actually
get it out there,
and it's just been a question
of too few engineers
being pulled
in too many dimensions.
But yeah, UiBinder is used
by AdWords.
Wave uses it.
And getting those guys
to ship
has kept managing to trump
actually open sourcing
the thing.
It will go out.
There's also, in AdWords,
there's some code that we rolled
to try to--
I implied a lot of Glue there.
Of...here's my phone object,
here's my string display,
copy the string to here,
copy the string back,
and, you know,
that kind of stuff.
We managed to generate
some amount of that code
in AdWords.
We weren't super thrilled--
or I wasn't super thrilled
with what we came up with.
But we've got notions there too
of frameworks
we'd like to provide
to kind of minimize that need.
But I don't have
particular promises
or particular dates to say.
We're aware of it
as an issue.
And we'll get there.
man: Hi.
Thank you for your talk.
Ryan: Thank you.
man: So the application
that we work on,
we've done something
similar to this
with something that roughly
translates to the Event Bus.
But we don't have all the other
constituent pieces
talking to the services.
Instead, they--
there's, like,
this back and forth
propagation of events.
And can you comment
on why that would be--
Ryan: If it's working for you,
I wouldn't tell you
to change it
just for the sake of changing.
We...this worked nicely
for us
because it...
I think we have no more events
than we actually need
traveling around the place,
and...
Again, you know,
for the testing
and for configuration purposes,
the contact list
can stand on its own
without having to know
who, actually,
it will be calling
into display contacts
and that kind of a thing.
If you don't have a problem,
there's no reason for you
to find a solution.
So...
man: All right, thank you.
Ryan: Yeah. Hi.
man: Hi. One of the
classic patterns
that people use
while writing UI code,
especially
in JavaScript libraries
like YUI
and other things like that,
is having data objects,
i.e. beings,
that they can then take
into a widget
that is able to take
that data object
and using
a reflection-like mechanism
of one sort or another
display things
into the widget.
Manipulate the data,
potentially even edit it
then spew it back out.
There's nothing like that
today in GWT,
and I'm suspecting
very strongly
that you guys internally
at Google
have some way
of dealing with this
for the large apps
that you have.
And what are your plans
in the future
with respect to this?
Ryan: We do have kind of
part of that in AdWords.
Other teams...
AdWords is the...
maybe the only app,
or one of the only apps
at Google,
that's actually
this kind of classical,
J2EE-style app.
And so we don't tend
to have broad solutions
for those types of things.
We have the solution
that AdWords rolled
for their particular needs.
You've probably seen
some borderline flame fests
on the GWT contributors list
about data binding
and the need
for doing something about this.
The approach
that we have taken
is where you would have used
reflection,
we will decorate
bits of our UI
with annotations,
with the name
of the particular property
of the model object
that that part of the UI
wants to display,
and we can generate the code
that would have been
reflection-based
for--this text field
is tied to the name property.
Oh, this thing looks
like a JavaBean
and it has a GetName method.
I'll GetName, I'll call SetName
at appropriate times.
We want to make that better
to generate
more of that code
than we're generating
right now.
And there's also
similar problems
that are coming up
on the server side
with AppEngine
and the ORM objects
that you're probably
persisting through JDO.
A lot of people have found
that they've created
these crafty little objects
that get all nice
and persistent for them
on the server side,
and then they try
to serialize them
for GWT RPC,
and that doesn't work,
and how do I get around that
without making
separate copies
and calling set and get
and get and set back and forth?
I think there's going to be
a similar solution to that,
but we don't--
We haven't quite
come upon it yet.
But we are
thinking about it actively.
I'm sorry, Fred,
I was just told
I have to shut up now.
So thanks, everybody.
[applause]