Sep 23, 2009

Google Web Toolkit and Client-Server Communications

This article is written by Miguel Mendez

Communication Infrastructure

Frame
  • Module com.google.gwt.user.User provides Frame class
  • Pass information to the server by manipulating the URL
  • Retrieve responses from the Frame's content or the server can write script tags to be executed
  • History and browser compatibility issues to consider
  • Load events are not reliable across browsers; specifically in Safari 2

FormPanel
  • Module com.google.gwt.user.User provides FormPanel class
  • Provides interoperability with servers that accept traditional HTML form encoding
  • Data is sent asynchronously
  • Any Widget that implements HasName which is part of the FormPanel will have its data sent on submit
  • Enables file uploads
FileUpload Example

final FormPanel form = new FormPanel();

form.setAction("/myFormHandler");

// FileUpload requires the POST method, and multipart MIME

// encoding.

form.setEncoding(FormPanel.ENCODING_MULTIPART);

form.setMethod(FormPanel.METHOD_POST);

// Create a FileUpload widget.

FileUpload upload = new FileUpload();

upload.setName("uploadFormElement");

form.setWidget(upload);

// Get a root panel and add the form and a button

RootPanel rootPanel = RootPanel.get();

rootPanel.add(form);

rootPanel.add(new Button("Submit", new ClickListener() {

public void onClick(Widget sender) {

form.submit();

}

}));

RequestBuilder (XHR)
  • Module com.google.gwt.http.HTTP provides RequestBuilder
  • Builder for making HTTP GETs and POSTs requests
  • Asynchronous communications only
  • Restricted by the same origin policy
  • Browsers limit the possible number of simultaneous connections so don’t go crazy firing off requests
RequestBuilder Example
public void onModuleLoad() throws RequestException {

String url = GWT.getModuleBaseURL() + "get";

RequestBuilder builder =

new RequestBuilder(RequestBuilder.GET, url);

// Create a callback object to handle the result

RequestCallback requestCallback = new RequestCallback() {

public void onError(Request request, Throwable exception) {

//…

}

public void onResponseReceived(Request request,

Response response) {

//…

}

};
// Send the request

builder.sendRequest("payload", requestCallback);

}

XML Services

XML Encoding/Decoding
  • Module com.google.gwt.xml.XML declares XML related classes
  • XMLParser parses a string containing valid XML into a new Document instance
  • Document class can be used to explore and modify the structure of the document
  • Document class will also convert the structure back into a string
  • Manipulation of XML is somewhat laborious
XMLRPC Example
RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, "...");

RequestCallback requestCallback = new RequestCallback() {

public void onResponseReceived(Request request,

Response response) {

// Parse xml response into a Document object

Document result = XMLParser.parse(response.getText());

// ...

}

// Error handling omitted

};

// Create Document

Document doc = XMLParser.createDocument();

// Add elements to the document as necessary…

// Send the XML request
rb.sendRequest(doc.toString(), requestCallback);

JSON Services

JSON Encoding/Decoding
  • Module com.google.gwt.json.JSON declares JSON related classes
  • JSONParser converts between strings and JSON objects
  • JSON is a fundamental data encoding that does not support cyclic structures
  • JSONP, JSONRPC are protocols built on top of the JSON encoding
  • Again, the conversion to/from JSON can be somewhat laborious
JSON Service Example
RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, "...");

RequestCallback requestCallback = new RequestCallback() {

public void onResponseReceived(Request request,

Response response) {

// Parse json response into a JSONValue object

JSONValue result = JSONParser.parse(response.getText());

// ...

}

// Error handling omitted

};

rb.sendRequest("{...}", requestCallback);

Efficiency Tip: JavaScriptObject Overlays

Overlays result in no runtime overhead; very efficient
/**

* Java overlay of a JavaScriptObject, whose JSON

* representation is { count: 5 }.

*/

public class MyJSO extends JavaScriptObject {

// Convert a JSON encoded string into a MyJSO instance

public static native MyJSO fromJSONString(

String jsonString) /*-{

return eval('(' + jsonString + ')');

}-*/;

// Returns the count property of this MyJSO

public native int getCount() /*-{

return this.count;

}-*/;

}

GWT RPC

GWT RPC Overview
  • Designed to move Java instances between client code (in the browser) and a Java servlet
  • Uses Serializable and IsSerializable marker interfaces
  • Interfaces define the service, a generator creates the necessary marshaling code with built-inversioning and a serialization policy file
  • Supports Java 1.5 language constructs
  • Built on top of RequestBuilder (XHR)
  • Like the rest of GWT, recompile to pick up the latest performance improvements - faster serialization code, etc.
Declaring GWT RemoteServices
// Implemented by the servlet

@RemoteServiceRelativePath("tasks")

public interface TaskRemoteService extends RemoteService {

List<Task> getTasks(int startIndex, int maxCount)

throws TaskServiceException;

}

// Implemented by generated client proxy, needs to match sync

public interface TaskRemoteServiceAsync {

void getTasks(int startIndex, int maxCount,

AsyncCallback<List<Task>> callback);

}

// TaskRemoteService servlet

public class TaskRemoteServiceImpl extends RemoteServiceServlet
implements TaskRemoteService {

public List<Task> getTasks(int startIndex, int maxCount) 
throws TaskServiceException {
// Code omitted
}
}

Invoking GWT RemoteServices

// Get client proxy, annotation causes auto addressing

TaskRemoteServiceAsync service =

GWT.create(TaskRemoteService.class);

// Create a callback object to handle results
AsyncCallback<List<Task>> asyncCallback =
new AsyncCallback<List<Task>>() {
public void onFailure(Throwable caught) {
// Deal with TaskServiceException...
}

public void onSuccess(List<Task> result) {
for (Task task : result) {
// Process each task...
}
}
};

// Actually call the service
service.getTasks(0, 10, asyncCallback);

Accessing the Request Object
  • Async method signature changed to return a Request instance
  • Useful for canceling the HTTP request used by RPC
// Modified async interface
public interface TaskRemoteServiceAsync {
// Method returns the underlying HTTP Request instance
Request getTasks(int startIndex, int maxCount,
AsyncCallback<List<Task>> callback);
}

Accessing the RequestBuilder Object
  • Change the return type of the async method to RequestBuilder, proxy returns a fully configured RequestBuilder
  • Provides access to HTTP timeouts, and headers
  • Caller must call RequestBuilder.send()
  • Wrap modified async interface to provide your own special manipulation code
// Modified async interface
public interface TaskRemoteServiceAsync {
// Method returns the underlying HTTP RequestBuilder instance
RequestBuilder getTasks(int startIndex, int maxCount,
AsyncCallback<List<Task>> callback);
}

Raw RPC Serialization
  • For pre-serialization of responses or custom transports
    * Client accesses the generated SerializationStreamFactory
    * Server uses RPC.encodeResponseForSuccess method to encode
  • Streams are not symmetric

public Object clientDeserializer(String encodedPayload)
throws SerializationException {
// Create the serialization stream factory
SerializationStreamFactory serializationFactory =
GWT.create(TaskRemoteService.class);
// Create a stream reader
SerializationStreamReader streamReader =
serializationFactory.createStreamReader(encodedPayload);
// Deserialize the instance
return streamReader.readObject();
}

Best Practices
  • Use stateless servers - better handling, better scalability
  • Keep conversational state in the client
  • Consider possible failure modes, keep the user’s needs in mind
  • Judiciously control the amount of data sent to the client - consider pagination of data instead of one bulk transfer

0 comments:

Text Widget

Copyright © Vinay's Blog | Powered by Blogger

Design by | Blogger Theme by