Remote Procedure Calls in GWT

currygeckoΛογισμικό & κατασκευή λογ/κού

2 Δεκ 2013 (πριν από 3 χρόνια και 6 μήνες)

71 εμφανίσεις

Remote Procedure Calls in GWT


How they are implemented


How to use them effectively


Best practices and design patterns


Future directions and possibilities


Discussion

Introduction


Your presenter: Rob Jellinghaus


Contributor to GWT


Refactored server
-
side RPC implementation in GWT
1.4


Architect at SF startup


http://robjsoftware.org

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

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

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);

}

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

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

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__());




}

GWT serialization vs. others


GWT serialization:


Fully statically compiled, metadata
-
guided


No class hooks


Java serialization:


Reflection
-
based


readObject, writeObject hooks


Java persistence serialization:


Reflection
-
based, metadata
-
guided


Bytecode proxies injected

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

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)

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.";


}…

}

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?


Using GWT RPC Effectively


We’ve covered the basics


Now for best practices


Responsiveness


Asynchrony


Serialization interactions


API design and service orientation

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

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);


}

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

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

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

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

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

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

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)

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);


}…

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

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

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”

The Home Stretch


Cool tricks


GWT 1.5


Future possibilities


Ranging from “sensible” to “wildly ambitious”


Other interesting systems


Conclusion

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

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

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

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…?

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”?

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

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)

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!!!