1. Remote Procedure Calls in GWT
• How they are implemented
• How to use them effectively
• Best practices and design patterns
• Future directions and possibilities
• Discussion
2. Introduction
• Your presenter: Rob Jellinghaus
• Contributor to GWT
– Refactored server-side RPC implementation in GWT
1.4
• Architect at SF startup
• http://robjsoftware.org
3. What is GWT RPC?
• Simple way for your GWT clients to call your
Java server code
• Looks a lot like regular Java procedure calls;
can pass complex objects easily
• Will skim over the basics
– GWT documentation is good
• Won’t address security
– Too large a topic
– Covered elsewhere at this conference
4. All Abstractions Leak
• Abstractions are great, but hidden details have a
way of surfacing
– Spolsky’s Law: All non-trivial abstractions, to
some degree, are leaky
• GWT RPC is no exception
– At least GWT is upfront about such things!
• Forewarned is forearmed
5. Asynchrony
• The biggest difference: GWT RPC returns immediately
• Normal procedure call:
Object result = myService.doWork();
doSomethingWith(result);
• GWT RPC:
myService.doWork(myCallback);
… wait a while …
public void onSuccess(Object result) {
doSomethingWith(result);
}
6. Asynchrony Is Your Friend
• The very definition of AJAX
– Old news to AJAX hackers, but unusual in the Java
world
• If your app isn’t waiting for the server, then
your users aren’t either
• Don’t work around it, embrace it!
– Workarounds are horrible and user-hostile
7. GWT RPC Lives in the Compiler
• RPC is implemented as a compiler extension
– See Ray Cromwell’s talk on “Generators”
• Enables very highly optimized RPC code
– Compiler looks at your interfaces and object types
– Emits tightly tuned encoding / decoding Javascript
• If GWT can’t compile it, you can’t send it
– Unlike RMI / Java serialization, where you can send
objects for which you don’t have source
8. Very brief example
• This class:
public class Entry implements com.google.gwt.user.client.rpc.IsSerializable{
private dto.domain.Blog blog;
private java.lang.String body;
…
}
• Gets this generated deserializer (details elided):
function dto_domain_Entry_1FieldSerializer_deserialize(streamReader, instance) {
dto_domain_Entry_1FieldSerializer_setBlog (instance,
com_google_gwt_lang_Cast_dynamicCast__Ljava_lang_Object_2I…
(streamReader.readObject__(), 17));
dto_domain_Entry_1FieldSerializer_setBody… (instance,
streamReader.readString__());
…
}
10. Server integration: basics
• Simplest technique: RemoteServiceServlet
public interface MyServiceInterface { public Object
doWork(); }
public class MyServiceServlet extends RemoteServiceServlet
implements MyServiceInterface {
public Object doWork() { … }
}
• Works nicely out of the box
• Requires one servlet class per interface you expose
– Doesn’t work well to expose pre-existing components
– Fixed with my refactorings in GWT 1.4
11. Server integration: Spring
• Several Spring integrations exist
– George Georgovassilis & Rob Hanson’s GWT-SL
server library
– Chris Lee’s one-page integration
• Couples with Spring’s “handler” mechanism for
web requests
• Can expose pure POJOs (GWT-SL), or can
annotate service classes (Chris Lee)
12. Server integration: Seam
• Seam 2.0 has built-in GWT integration
– GWTService web resource
• Set your endpointURL to be “seam/resource/gwt”
• RPC requests route to a Seam component by interface
name:
@Name("org.jboss.seam.example.remoting.gwt.client.MyService")
public class ServiceImpl implements MyService
{
@WebRemote
public String askIt(String question)
{
return "42. Its the real question that you seek now.";
}…
}
13. Server integration: Seam/JSF + GWT
• Wrap your GWT module in a JSF component
• Drop it into a JSF page
• Route its RPC to any Seam component:
<!-- routes to the BlogService on the gwtBlog component -->
<bloglist:component id="main2">
<gwt:gwtListener serviceBean="#{gwtBlog}"/>
</bloglist:component>
• Only one problem… broken with Seam 2 at the
moment
– Anyone want to help?
14. Using GWT RPC Effectively
• We’ve covered the basics
• Now for best practices
– Responsiveness
– Asynchrony
– Serialization interactions
– API design and service orientation
15. Responsiveness: Balanced RPC
• Messages Want To Be Small
– Small messages are quick to process
– Low encoding & decoding overhead
• But Messages Want To Be Big
– Networks are slow
– Many messages = much exposure to network latency
• Balance your RPC
– Only pull data visible to the user; paginate on server
– Don’t build too many UI elements either
16. Asynchrony: No More Straight Lines
• Synchronous code might look like this
– (can’t use Java 5 yet in GWT 1.4):
Blog blog = blogService.getBlog();
List entries = blogService.getEntries();
for (int i = 0; i < entries.size(); i++) {
BlogEntry entry = (BlogEntry)entries.get(i);
TreeItem item = new TreeItem(entry.getTitle());
tree.addItem(item);
String body = blogService.formatText(entry.getId());
entry.setBody(body);
}
17. Fun with Inner Classes
• Anonymous inner classes split up the code inline:
Blog blog = null;
List entries = null;
blogService.getBlog(new AsyncCallback() {
public void onSuccess(Object result) {
blog = (Blog)result;
blogService.getEntries(new AsyncCallback() {
public void onSuccess(Object result) {
entries = (List)result;
}
});
}
});
• But gets very nested and tough to read
18. Callback Objects
• Break out the callbacks into helpers:
Blog blog = null;
blogService.getBlog(new AsyncCallback() {
public void onSuccess(Object result) {
blog = (Blog)result;
blogService.getBlogEntries(makeEntriesCallback());
}
});
public AsyncCallback makeEntriesCallback() {
return new AsyncCallback() {
public void onSuccess(Object result) {
List entries = (List)result;
…
• Makes the sequence more descriptive
19. Stateful Callback Objects
• Multiple RPCs in flight at once:
List entries = (List)result;
for (int i = 0; i < entries.size(); i++) {
BlogEntry entry = (BlogEntry)entries.get(I);
TreeItem item = new TreeItem(entry.getTitle()); tree.addItem(item);
blogService.fetchText(entry.getId(), makeEntryCallback(item));
…
}
public AsyncCallback makeEntryCallback(TreeItem item) {
return new AsyncCallback() {
public void onSuccess(Object text) {
item.setText((String)text); } } }
• Each callback knows what to do with its response
• Can extend this pattern to all kinds of sequences
20. Transactions
• Updates can sometimes require multiple server
calls
• Keep state in your client until commit time
– The less server state the better
– But can’t send too much state at once when
committing
• Can use “chained command” pattern in your
service
– Send a whole sequence of API calls
– Rather like offline sync in Google Gears
21. When Abstractions Attack
• GWT makes it easy to call your Java backend
• Many Java backends use Hibernate, JPA, EJB3…
• GWT abstraction: serialize object graph to Javascript
– Needs to see all the source
• JPA abstraction: load partial object graph lazily
– Create secret hidden $$CGLIB classes, private collections
• GWT + JPA: KABOOM!
– GWT can’t serialize a lazy persistence proxy or Hibernate
PersistentMap
– Spolsky’s Revenge
22. DTOs: Back to the Future
• Data transfer objects: intermediate object layer
• Copy your persistent objects into separate DTO
structure
– GWT only sees the DTO objects
– Explicit control over what gets sent
– Can limit features used in DTO objects (avoid
generics, annotations)
– Risk of lots of boilerplate code
23. Generated DTOs: Less Boilerplate
• Automatically generate DTOs at build time
– Codehaus JAM project: Java source walker
– Compile DTOs into a standalone GWT module
for (JField field : jClass.getFields()) {
String name = field.getSimpleName();
String type = field.getType().getQualifiedName();
if (type.startsWith("java.")) {
// skip over classes that aren't emulated by GWT…
• Reflection can populate DTOs
– Kind of an intermediate “serializer” from persistent objects to
DTO objects
– Hibernate4gwt project allows this (with merging, too)
24. Rocket Science: Customizing RPC
• GWT ServerSerializationStreamWriter
– Custom GWT class that traverses objects
– Can make subclass with special handling of persistent classes
• Null out lazy collections, load lazy proxy objects
– Risks breaking if GWT changes RPC implementation
public void serializeValue(Object value, Class type) throws
SerializationException {
…
else if (type == java.util.Set.class) {
Set hashSet = new HashSet();
if (value instanceof PersistentSet) {
PersistentSet persSet = (PersistentSet) value;
if (persSet.wasInitialized()){
hashSet.addAll(persSet);
}…
25. The Trouble with Merging
• Once you send your objects back up, you have to merge
them
• Seam / JPA stateful dogma says this is a weakness of
GWT and other rich clients
– Stateful apps keep persistence context around while user is
interacting; dirty objects tracked for free
• But persistence contexts die badly if commit fails
– Only solution is to abandon all your modified state!
• GWT APIs need to clearly identify what’s changed
– Simplifies the merge problem
– Arguably easier to recover from conflicts
– Scales better, too
26. DAOs are not APIs
• Tempting to just expose your persistence layer
• Don’t do this!
– Persistence operations are not a good API
• APIs should be intentional
– Should be tuned to application use cases
– Objects exposed should be client-meaningful
• May or may not be your domain objects
– Think services rather than data accesses
• Service-oriented backends are more scalable
27. Serializable != IsSerializable
• Java serialization is not GWT serialization
– Java serialization expects a very particular contract
– Hardcoded to binary / byte level; lots of existing
readObject, writeObject methods
– Those methods not necessarily compilable by GWT
– Java serialization not efficient in Javascript
• In GWT, “java.lang.Serializable” just means
“OK to send by GWT RPC”
– Does NOT mean “Will use existing readObject /
writeObject implementations”
28. The Home Stretch
• Cool tricks
• GWT 1.5
• Future possibilities
– Ranging from “sensible” to “wildly ambitious”
• Other interesting systems
• Conclusion
29. Preserialized objects
• You’ve got a bunch of initialization data
– You want to download it efficiently
– Maximum-speed startup, one round-trip
• iPhone apps, anyone?
• Serialize the data on the server, then save the
bytes
• Blast them out from cache, then deserialize
them explicitly
– In GWT 1.5: get SerializationStreamReader from
client-side RPC proxy
30. GWT 1.5: Java 5 for the win!
• Plan: full support for enumerated types, generic
collections
• No more @gwt.typeargs
– Typed collections get optimized RPC automatically
• Annotations are OK
– Not yet clear what GWT will use them for
– Compiler should be able to ignore annotations w/o
available source
• So @Entity, @Component, etc. won’t choke GWT
31. Possibility: RPC metadata
• Support for request/response headers in RPC
messages
• Use cases:
– Servers that use HTTP headers for authorization
– Support for Spring webflow / Seam conversations
• API totally undefined:
– Stateful instantiation of Service? Callback functions?
– Related to RPC cancellation / request control?
– Discuss on GWT Contributors forum
32. Possibility: JSON-style encoding
• GWT compiler generates current deserializers
– Pretty much the best Javascript functions can do
– But still not as good as JSON
• What if RPC payloads were deserialized by the
browser, as with JSON?
– Could be blazingly fast deserialization
– AND less client-side code
– But lots of issues to work out (see GWTC thread)
• Not high priority for GWT 1.5, but later…?
33. Possibility: Declarative data binding
• Right now RPC is strictly imperative
• Some modern frameworks (Seam) support
declarative data binding
– Refer to server data objects by name
– UI components bind to specific sub-objects
• Declarative UI support underway for GWT;
perhaps data binding a natural extension?
– Could also integrate some support for offline
synchronization?
– Pagination done “under the hood”?
34. Possibility: Generalized mappings
• Persistence mapping is very similar to client-server
DTO mapping
– In both cases, you have data spaces containing objects that
must be transferred and correlated
• Why not a unified answer?
– What if persistence mappings could be extended to define data
transfer behaviors to the client?
– And then further, to define client-side update sets and
synchronization behaviors?
– Attack the DTO problem at the framework level!
• See LINQ 2.0 work from Microsoft Research
35. Other interesting systems
• Good old RMI (Sun)
– Classloader problems much worse than widely known
• Caja (Ben Laurie, Mark Miller, Google)
– Capability-secure Javascript
– Mobile code, with non-broken sandboxes
– Influenced by E (asynchronous capability messaging)
– (GWT -> Caja scriptlets???)
• LINQ 2.0 (Erik Meijer, Microsoft)
– Automatically restructure sequential client app to be
asynchronous multi-tier app
– Integrate SQL queries, XML mappings, RPC
– MSIL -> Javascript (like GWT for .NET bytecode)
36. Thanks!
• Hope this was helpful
• Thanks to GWT team & Pearson
• http://robjsoftware.org/gwt2007
– links & further references
• Go forth and create great apps!
– And then post about them on the GWT groups!
– And then start contributing to GWT!!!
Hinweis der Redaktion
Welcome to this talk about remote procedure calls in GWT. I’ll be starting with a look at GWT’s RPC implementation, which has some tradeoffs that directly affect your code. I’ll also cover how best to use RPC in GWT, and how to structure your applications to leverage it most effectively, in terms of best practices and design patterns you may find useful. Finally, I’ll talk about some possible RPC extensions and future features that are under consideration for GWT 1.5, and finally leave some time for discussion.
A quick introduction: My name is Rob Jellinghaus. I’m up here because I am a GWT contributor; I started working with GWT late last year, and wanted to integrate it with Seam. I rapidly discovered that the GWT server-side RPC implementation wasn’t well structured to expose pre-existing server components. I proposed a refactoring for it and implemented it, with lots of excellent review from Miguel Mendez on the GWT team. So I’ve got some familiarity with RPC’s internals. I’m also an architect at a San Francisco startup where we’re doing a good amount of rich client work, so I’m acquainted with the problem space. I’m honored to be included in this conference, thanks guys!
So what is GWT RPC? It’s a built-in mechanism in GWT for your clients to call your Java server code. You expose an interface to the server, you call it from the client, you can pass complex Java objects back and forth. Like everything in GWT, it’s continually getting tweaked for higher performance. Overall, it’s a powerful abstraction. Since many of you have worked with it already, I won’t be giving detailed examples of how to set up GWT RPC; the GWT documentation covers the basics effectively.
GWT RPC does its best to make it convenient to call your server code, by abstracting away many of the details. But Joel Spolsky has a well-known law that all abstractions leak. GWT RPC is no exception. This isn’t a slam on GWT; we’ll see plenty of other non-GWT leaky abstraction examples later in this talk. One of GWT’s strengths is that it doesn’t try to push its abstractions beyond their limits; GWT is very upfront about what realities it can and can’t hide from you. Being aware of these issues will let you manage them effectively.
Still moving quickly through the basics: GWT RPC is asynchronous, which means you don’t get results right away. Results come back later, and are passed to a callback handler you define. Results from the server look more or less just like user interface events from your user. This completely changes how you write your code.
This asynchronous RPC is a very, very good thing. It’s so good that it’s the very definition of AJAX. If your app isn’t waiting for the server, then your users aren’t either. Later in the talk I’ll detail some useful patterns for leveraging asynchrony.
The other key thing to know about GWT RPC is that, unlike many other RPC systems, it is built into the GWT compiler. It uses the GWT compiler’s “generators” technology (see Ray Cromwell’s talk) to emit customized serialization code for each of the types that you send over RPC. This code knows exactly what fields and references your RPC types contain, and is tuned to run as fast as possible in the browser’s scripting engine.
Just to make this concrete, here’s an example from a GWT demo application I’ll be showing. It’s Yet Another Blogging App, and it has a class for blog entries that looks like this. The GWT compiler emits RPC deserialization code that looks like this. It walks through each field of a BlogEntry pulling its data from the stream. This code is straight-line Javascript that has no conditionals or dispatching, and that therefore runs very fast.
Just to make this concrete, here’s an example from a GWT demo application I’ll be showing. It’s Yet Another Blogging App, and it has a class for blog entries that looks like this. The GWT compiler emits RPC deserialization code that looks like this. It walks through each field of a BlogEntry pulling its data from the stream. This code is straight-line Javascript that has no conditionals or dispatching, and that therefore runs very fast.
At some point you need to couple GWT RPC to your server code. The simplest technique for doing this is to subclass RemoteServiceServlet.
Spolsky: http://www.joelonsoftware.com/articles/LeakyAbstractions.html Chris Lee / Spring annotation exposure: http://blog.digitalascent.com/2007/11/gwt-rpc-with-spring-2x_12.html GWT-SL / Spring POJO exposure: http://gwt-widget.sourceforge.net/?q=node/39 Seam & GWT: http://docs.jboss.com/seam/2.0.0.GA/reference/en/html/gwt.html Seam & GWT & JSF: http://unrealities.com/seamgwt Custom StreamWriter: http://groups.google.com/group/Google-Web-Toolkit-Contributors/browse_thread/thread/669e3d31b60bb1be Cancelling RPC: http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/5d94093b34b65a71 Client-side explicit deserialization: http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/a192fb396cc2df0b# Directly-eval'able server responses: http://groups.google.com/group/Google-Web-Toolkit-Contributors/browse_thread/thread/761f743d2d7471ee RMI problems: http://research.sun.com/techrep/2007/smli_tr-2007-162.pdf http://research.sun.com/techrep/2006/smli_tr-2006-149.pdf Caja: http://www.links.org/?p=271 E: http://www.erights.org/ LINQ 2.0: http://research.microsoft.com/~emeijer/ http://www.itwriting.com/blog/?p=158 http://research.microsoft.com/~emeijer/Papers/DemocratizingTheCloudOOPSLA2007.pdf