gwt web socket and data serialization

27
Michele Ficarra GWT Web Socket and data serialization Software Engineer at Thales Italy S.p.A

Upload: gwtcon

Post on 13-Jul-2015

1.183 views

Category:

Technology


1 download

TRANSCRIPT

Michele Ficarra

GWT Web Socket and data serialization

Software Engineer at Thales Italy S.p.A

Case study

Let's focus on the Browser <-> Server communication

With GWT this!is easy

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

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 …

Push data from the server to browser!

isn’t so easy…

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

Full working example available on: !https://github.com/michelefi/gwtcon-websocket

Questions?

That’s all, happy hacking and thanks for the attention.