Date post: | 13-Jul-2015 |
Category: |
Technology |
Upload: | gwtcon |
View: | 1,181 times |
Download: | 1 times |
From gwtproject.org
“GWT provides an RPC mechanism based on Java Servlets to provide access to server-side
resources. This mechanism includes generation of efficient client-side and server-side code to
serialize objects across the network using deferred binding.”
GWT RPC• Easy to write, hide all AJAX and serialization
complexity. !
• You can use the same POJO in client and server, you need only to implement the serializable interface.!
• But …
Possible solutions• Polling!
• HTTP Long Polling (COMET)!
• Web Socket
Two RFC can help us to choose
From RFC 6202 - Bidirectional HTTP - April 2011
“On today's Internet, the Hypertext Transfer Protocol (HTTP) is often used (some would say
abused) to enable asynchronous, "server-initiated" communication from a server to a
client as well as communication from a client to a server.”
From RFC 6455 - The WebSocket Protocol - December 2011
“The WebSocket protocol consists of an opening handshake followed by basic message
framing, layered over TCP. The goal of this technology is to provide a mechanism for
browser-based applications that need two-way communication with servers”
• WebSocket seems to be the right choice, but GWT doesn’t provide natively a way to use it!
• On Internet we can find a lots of library that can help:!• Errai - http://erraiframework.org/!• Gwt-ws - https://code.google.com/p/gwt-ws/!• …!
• But this time we want to make our hands “dirty” and try to do it by ourself!
Web Socket - Client Side
var websocket = new WebSocket("ws://locahost:8025/echo");!websocket.onopen = function(evt) { console.log("WebSocket open");};websocket.onclose = function(evt) { console.log("WebSocket close");};websocket.onmessage = function(evt) { alart(evt.data);};websocket.onerror = function(evt) { console.log("WebSocket error");};!websocket.send("Hello World!");
JAVASCRIPT
Web Socket - Client Side
package ws;import com.google.gwt.core.client.JavaScriptObject;!public abstract class WebSocket {! private JavaScriptObject ws;! public WebSocket(String url) { ws = init(url); }! abstract void onClose(); abstract void onError(); abstract void onMessage(String msg); abstract void onOpen();! private native JavaScriptObject init(String url) /*-{ . . . }-*/;! native void send(String message) /*-{ [email protected]::ws.send(message); }-*/;}
JAVA
For use the previous code with GWT we need to write a JSNI wrapper
Web Socket - Client Side
private native JavaScriptObject init(String url) /*-{ var websocket = new WebSocket(url); var wrapper = this; websocket.onopen = function(evt) { [email protected]::onOpen()(); }; websocket.onclose = function(evt) { [email protected]::onClose()(); }; websocket.onmessage = function(evt) { [email protected]::onMessage(Ljava/lang/String;)(evt.data); }; websocket.onerror = function(evt) { [email protected]::onError()(); }; return websocket; }-*/;
JAVA
Web Socket - Server Side
• For develop the server side we can use the Java API for WebSocket (JSR 356), that is part of the Java EE 7 standard!
• If we want use the JSR 356 without a full JEE 7 container it’s possibile to embed a WS server like Tyrus - https://tyrus.java.net/
<dependency> <groupId>org.glassfish.tyrus</groupId> <artifactId>tyrus-server</artifactId> <version>1.8.3</version> </dependency> <dependency> <groupId>org.glassfish.tyrus</groupId> <artifactId>tyrus-container-grizzly-server</artifactId> <version>1.8.3</version> </dependency>
MAVEN
Web Socket - Server Side
Example of an Echo Web Socket service with JSR 356
package ws;!import javax.websocket.OnMessage;import javax.websocket.server.ServerEndpoint;!@ServerEndpoint("/echo")public class EchoEndpoint {! @OnMessage public String echo(String message) { return message + " (from your server)"; }!}
JAVA
Web Socket - Server Side
Start Tyrus Server in Jetty 6 (DevMode)
package ws;!import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.websocket.DeploymentException;!import org.glassfish.tyrus.server.Server;!public class InitListener implements ServletContextListener {! private Server server = null;! @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { server.stop(); }! @Override public void contextInitialized(ServletContextEvent servletContextEvent) { try { server = new Server("localhost", 8025, "/", null, EchoEndpoint.class); server.start(); } catch (final DeploymentException e1) { e1.printStackTrace(); } }}
JAVA
<listener> <listener-class>ws.InitListener</listener-class> </listener>
WEB.XML
Data Serialization• Now we are capable of send string back and
forward from the browser to the server and viceversa!
• But if we want use this technology in a complex environment this result is a little poor. We want send Object not String!!
• The first idea to fix the problem can be to use JSON serialization…
• … and after a little search we found a lots of way to do that:!• http://www.gwtproject.org/doc/latest/tutorial/JSON.html!• https://github.com/nmorel/gwt-jackson!• …!
• But every solution require a lot of boilerplate code or adding annotation to the data model!
• We would like something more similar to the GWT RPC, that require only the Serializable interface. May be it can be reused?
• After searching in the web and in the GWT source code the solution come out!
• We need to define the interface of an RPC and reuse the serialization engine already present in GWT
Data Serialization
public class Message implements Serializable {! private String data; private String username; private Date time;! /* ... */ }
JAVA @RemoteServiceRelativePath("MessageService") public interface MessageService extends RemoteService { Message getMessage(Message message);! }
JAVA
The RPC Serialization is designed for function call. The client serialize the function argument and the server the
return value. So, if we want exchange the same object, it’s important that in the service definition the args and the
return value are the same class
public String serializeMessae(Message message) { try { SerializationStreamFactory factory = (SerializationStreamFactory) GWT.create(MessageService.class); SerializationStreamWriter writer = factory.createStreamWriter(); writer.writeObject(message); final String data = writer.toString(); return data; } catch (final SerializationException e) { e.printStackTrace(); } return null; }
JAVA
Data Serialization - Client Side
Here there is the trick, the Async Service that is usual return by the deferred binding is also an instance of a
SerializationStreamFactory. That can be used for serialize and deserialize objects
public Message deserializeMessae(String data) { try { SerializationStreamFactory factory = (SerializationStreamFactory) GWT.create(MessageService.class); final SerializationStreamReader streamReader = factory .createStreamReader(data); final Message message = (Message) streamReader.readObject(); return message; } catch (final SerializationException e) { e.printStackTrace(); } return null; }
JAVA
Data Serialization - Client Side
Data Serialization - Server Side
private Message deserializeMessage(String data) throws SerializationException { ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader( Thread.currentThread().getContextClassLoader(), new CustomSerializationPolicyProvider()); // Filling stream reader with data streamReader.prepareToRead(data); // Reading deserialized object from the stream final Message message = (Message) streamReader.readObject(); return message; }
JAVA
On server side is more or less the same of the client. We need an instance of a ServerSerializationStreamReader for
read the object.
The only hack is how create a CustomSerializationPolicyProvider
Data Serialization - Serialization Policy
public class CustomSerializationPolicyProvider implements SerializationPolicyProvider {! @Override public SerializationPolicy getSerializationPolicy(String moduleBaseURL, String serializationPolicyStrongName) { return new SimpleSerializationPolicy(); }!}
JAVA
public class SimpleSerializationPolicy extends SerializationPolicy {! @Override public boolean shouldDeserializeFields(Class<?> clazz) { return isSerializable(clazz); }! @Override public boolean shouldSerializeFields(Class<?> clazz) { return isSerializable(clazz); }! /* ... */! private boolean isSerializable(Class<?> clazz) { if (clazz != null) { if (clazz.isPrimitive() || Serializable.class.isAssignableFrom(clazz) || IsSerializable.class.isAssignableFrom(clazz)) { return true; } } return false; }}
JAVA
RPC generates a serialization policy file during GWT compilation. The serialization policy file contains a whitelist of allowed types which may be serialized.!In this simple implementation there is only the check of the Serializable interface.!Watch out of what are you serializing or you can perform problem on client side.
Data Serialization - Server Side
private String serializeMessage(final Message messageDto) throws SerializationException {! ServerSerializationStreamWriter serverSerializationStreamWriter = new ServerSerializationStreamWriter(new SimpleSerializationPolicy());! serverSerializationStreamWriter.writeObject(messageDto); String result = serverSerializationStreamWriter.toString(); return result; }
JAVA
Data Serialization - OnMessage
@OnMessage public void onMessage(String message, Session session) { try {! Message messageDto = deserializeMessage(message);! String result = serializeMessage(messageDto);! for (Session s : session.getOpenSessions()) { if (s.isOpen()) { s.getBasicRemote().sendText(result); } } } catch (final SerializationException | IOException e) { logger.log(Level.WARNING, "Error", e); } }
JAVA
Now we have all pieces for finish the OnMessage method
Deserialization of an incoming message
from a client
Serialization for the clients. The clients can deserialize only object
encoded by the sever due the request - response
nature of the RPC
Web Socket broadcast