Upload
lastrand
View
1.878
Download
0
Embed Size (px)
DESCRIPTION
Presentation about transport mechanisms in GWT, held at GWT.create 2013 in San Francisco and Frankfurt.
Citation preview
Leif ÅstrandSenior Vaadin Expert
Comparing GWT Transport Mechanisms
torsdag 19 december 13
public class Contact { private String name; private int yearOfBirth;
private List<String> emailAddresses;
private Address address; public static class Address { private String street; private String city; }
// + Getters and setters
}
Contact
torsdag 19 december 13
AJAX
torsdag 19 december 13
Good
• It just works
Bad
• Low level
RequestBuilder
torsdag 19 december 13
Real world usage
11 %torsdag 19 december 13
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);try { builder.sendRequest(requestData, new RequestCallback() {
@Override public void onResponseReceived(Request request, Response response) { int statusCode = response.getStatusCode(); String text = response.getText(); }
@Override public void onError(Request request, Throwable exception) { // TODO Handle asynchronous problems
} });} catch (RequestException e) { // TODO Handle synchronous problems}
RequestBuilder
torsdag 19 december 13
Contact
String
torsdag 19 december 13
String data = contact.getName();data += "," + contact.getYearOfBirth();
String[] parts = data.split(",");contact.setName(parts[0]);contact.setYearOfBirth(Integer.parseInt(parts[1]));
String conversion
torsdag 19 december 13
String data = contact.getName();data += "," + contact.getYearOfBirth();
String[] parts = data.split(",");contact.setName(parts[0]);contact.setYearOfBirth(Integer.parseInt(parts[1]));
String conversion
torsdag 19 december 13
<?xml version="1.0" encoding="UTF-8"?><contact name="John Doe" yearOfBirth="1900"> <address street="HappyStreet 1" city="Turku" /> <email address="[email protected]" /> <email address="[email protected]" /></contact>
XML
torsdag 19 december 13
Document document = XMLParser.parse(string);Element contactElement = document.getDocumentElement();
contact.setName(contactElement.getAttribute("name"));contact.setYearOfBirth(Integer.parseInt(
contactElement.getAttribute("yearOfBirth")));contact.setAddress(parseAddress(
contactElement.getElementsByTagName("address").item(0)));
NodeList emailTags = contactElement.getElementsByTagName("email");for (int i = 0; i < emailTags.getLength(); i++) { contact.getEmailAddresses().add(
((Element) emailTags.item(i)).getAttribute("address"));
}
XML parsing
torsdag 19 december 13
Good
• It’s “standard”• Lots of server-side
libraries
Bad
• Verbose data• Verbose code• Document markup
language• Not so common in
the GWT ecosystem• Not typesafe
XML
torsdag 19 december 13
{ name: "John Doe", yearOfBirth: 1900, address: { street: "Happy Street 1", city: "Turku" }, emailAddresses: ["[email protected]", "[email protected]"]}
JSON
torsdag 19 december 13
JSONObject json = JSONParser.parseStrict(string).isObject();
contact.setName(json.get("name").isString().stringValue());contact.setYearOfBirth(
(int) json.get("yearOfBirth").isNumber().doubleValue());contact.setAddress(
parseAddress(json.get("address").isObject()));
JSONArray emailAddresses = json.get("emailAddresses").isArray(); for (int i = 0; i < emailAddresses.size(); i++) { contact.getEmailAddresses().add(
emailAddresses.get(i).isString().stringValue()); }}
JSONValue parsing
torsdag 19 december 13
Good
• It’s “standard”• Extensive library
support• Compact format
Bad
• Not completely typesafe
• JSONValue parsing code is verbose
JSON
torsdag 19 december 13
public class ContactJso extends JavaScriptObject { public native String getName() /*-{ return this["name"]; }-*/;
public native int getYearOfBirth() /*-{ return this["yearOfBirth"]; }-*/;
public native AddressJso getAddress() /*-{ return this["address"]; }-*/;
public native JsArrayString getEmailAddresses() /*-{ return this["emailAddresses"]; }-*/;}
JavaScriptObject
torsdag 19 december 13
Good
• Very efficient• Very little boilerplate
Bad
• /*-{ ... }-*/; syntax• Can’t share code
with the server
JavaScriptObject
torsdag 19 december 13
What about the server?
torsdag 19 december 13
JAX-RS
torsdag 19 december 13
Good
• It’s “standard”• Full control
Bad
• About as verbose as JSONValue
• Not suitable for GWT
• The Software shall be used for Good, not Evil.
org.json
torsdag 19 december 13
ObjectMapper mapper = new ObjectMapper();try { Contact contact = mapper.readValue(string, Contact.class);} catch (VariousExceptions e) { // Do something sensible}
Jackson on the server
torsdag 19 december 13
ReflectionCode generation
torsdag 19 december 13
public static interface ContactMapper extends ObjectMapper<Contact> {}
public Contact parseContact(String string) { ContactMapper mapper = GWT.create(ContactMapper.class); Contact contact = mapper.read(string); return contact;}
gwt-jackson
torsdag 19 december 13
Good
• Minimal boiler plate• Can share code
between server and client
• Plugin for JAX-RS
Bad
• You’re still just sending objects
Jackson
torsdag 19 december 13
Using interfaces instead of objects
torsdag 19 december 13
public interface Contact { public void setName(String name); public String getName();
public void setYearOfBirth(int yearOfBirth); public int getYearOfBirth();
public void setAddress(Address address); public Address getAddress();
public void setEmailAddresses(List<String> addresses); public List<String> getEmailAddresses();}
Contact interface
torsdag 19 december 13
interface AddressBookFactory extends AutoBeanFactory { public AutoBean<Contact> contact(); }
public void autobeanExample() { AddressBookFactory factory = GWT.create(AddressBookFactory.class); AutoBean<Contact> contactBean = factory.contact(); Contact contact = contactBean.as();
contact.setName("John Doe"); contact.setYearOfBirth(1900); contact.setEmailAddresses(Arrays.asList("[email protected]", "[email protected]"));
String json = AutoBeanCodex.encode(contactBean).getPayload();
AutoBean<Contact> bean = AutoBeanCodex.decode(factory, Contact.class, json);
contact = bean.as(); }
AutoBean
torsdag 19 december 13
Good
• Flexible foundation for custom implementations of interface methods
Bad
• Can’t use classes
AutoBean
torsdag 19 december 13
Intercepting interface methods allows us to...
Send partial updates
Manage entity identity
Use instance methods for RPC
torsdag 19 december 13
public interface AddressBookRequestFactory extends RequestFactory { public ContactRequest contactRequest();}
@Service(Contact.class)public interface ContactRequest extends RequestContext { public Request<List<ContactProxy>> getContacts(); public InstanceRequest<ContactProxy, Void> update();}
public void setupRequestFactory() { this.factory = GWT.create(AddressBookRequestFactory.class); this.eventBus = new SimpleEventBus(); this.factory.initialize(this.eventBus);}
RequestFactory - setting it up
torsdag 19 december 13
public void contactRequest() { ContactRequest contactRequest = factory.contactRequest(); contactRequest.getContacts().with("address").fire( new Receiver<List<ContactProxy>>() { @Override public void onSuccess(List<ContactProxy> contacts) { updateUI(contacts); } });}
public void updateContact(ContactProxy proxy) { ContactRequest contactRequest = factory.contactRequest(); contactRequest.update().using(proxy).fire();}
RequestFactory -client-side usage
torsdag 19 december 13
public class Contact { // + fields, setters and getters public Integer getId() { return this.id; }
public Integer getVersion() { return this.version; }
public void update() { ContactDAO.update(this); }
public static List<Contact> getContacts() { return ContactDAO.fetchContacts(); }}
RequestFactory - server-side
torsdag 19 december 13
Good
• Entities do not need to be GWT compatible
• Combines RPC and entity management
• Automatic request handling
Bad
• Complex setup• Heavy coupling with
the server
RequestFactory
torsdag 19 december 13
Real world usage
7 %torsdag 19 december 13
Why not just send Java objects?
torsdag 19 december 13
public Object[] sendAndReceive(Object[] objects);
Send and receive objects
public interface ContactService { public void saveContact(Contact contact); public List<Contact> getContacts();}
torsdag 19 december 13
public interface ContactService { public AsyncResult<Void> saveContact(Contact contact); public AsyncResult<List<Contact>> getContacts();}
Asynchronousity
public interface ContactServiceAsync { public void saveContact(Contact contact, AsyncCallback<Void> callback); public void getContacts(AsyncCallback<List<Contact>> callback);}
torsdag 19 december 13
Good
• Simple but powerful concept
• The default solution
Bad
• You (almost) always send the entire object graph
GWT-RPC
torsdag 19 december 13
Most popular!
53 %torsdag 19 december 13
[-4, 2, 42, [“Foo”, “Bar”]]
2 | Foo | Bar | 42 | 2 | -4
torsdag 19 december 13
What if we put the server in control?
torsdag 19 december 13
torsdag 19 december 13
State synchronization
torsdag 19 december 13
public class ContactState extends SharedState { public String name;
@DelegateToWidget public int yearOfBirth;}
@Overridepublic TextButtonState getState() { return (TextButtonState) super.getState();}
addStateChangeHandler("name", new StateChangeHandler() { @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { String name = getState().name; doSomethingWithTheName(name); }});
Reacting to state changes
torsdag 19 december 13
Events
torsdag 19 december 13
public interface ContactRpc extends ServerRpc { public void deleteContact(int id);}
// Sending RPC from the clientpublic void sendDelete(int contactId) { getRpcProxy(ContactRpc.class).deleteContact(contactId);}
//Registering RPC handler on the serverregisterRpc(new ContactRpc() { @Override public void deleteContact(int id) { ContactDAO.deleteById(id); }});
RPC
torsdag 19 december 13
Good
• Stateful server• Server push
Bad
• Stateful server
Vaadin
torsdag 19 december 13
Real world usage
6 %torsdag 19 december 13
What if we completely hide the transport?
torsdag 19 december 13
Local? Remote? It’s all the same!
torsdag 19 december 13
// Fire event from the client or the server @InjectEvent<Contact> contactEvent;
public void updateContact(Contact contact) { contactEvent.fire(contact);}
// Listen to event on the client or the serverpublic void contactUpdateObserver(@Observes Contact contact) { ContactDAO.updateContact(contact);}
Errai CDI events
torsdag 19 december 13
Good
• Transparent communication
• Different protocols• Server push
Bad
• Transparent communication
Errai Bus
torsdag 19 december 13
Which one should I use?
torsdag 19 december 13
Did I get some detail wrong?
Questions?
Please rate the talk at gwtcreate.com/agenda? [email protected]
torsdag 19 december 13