A Beginners Guide to Building a RAG App Using Open Source Milvus
GWT Web Socket and data serialization
1. GWT Web Socket and
data serialization
Michele Ficarra
Software Engineer at Thales Italy S.p.A
2. Case study
With GWT this!
is easy
Let's focus on the Browser <-> Server communication
3. “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.”
From gwtproject.org
5. 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 …
7. Possible solutions
• Polling!
• HTTP Long Polling (COMET)!
• Web Socket
Two RFC can help us to choose
8. “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 6202 - Bidirectional HTTP - April 2011
9. “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”
From RFC 6455 - The WebSocket Protocol - December 2011
10. • 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!
12. Web Socket - Client Side
For use the previous code with GWT we need to write a JSNI wrapper
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) /*-{
this.@ws.WebSocket::ws.send(message);
}-*/;
}
JAVA
13. Web Socket - Client Side
private native JavaScriptObject init(String url) /*-{
var websocket = new WebSocket(url);
var wrapper = this;
websocket.onopen = function(evt) {
wrapper.@ws.WebSocket::onOpen()();
};
websocket.onclose = function(evt) {
wrapper.@ws.WebSocket::onClose()();
};
websocket.onmessage = function(evt) {
wrapper.@ws.WebSocket::onMessage(Ljava/lang/String;)(evt.data);
};
websocket.onerror = function(evt) {
wrapper.@ws.WebSocket::onError()();
};
return websocket;
}-*/;
JAVA
14. 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
15. 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
16. 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
WEB.XML
<listener>
<listener-class>ws.InitListener</listener-class>
</listener>
17. 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…
18. • … 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?
19. • 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
20. Data Serialization
public class Message implements Serializable {
!
private String data;
private String username;
private Date time;
!
/* ... */
}
JAVA
JAVA
@RemoteServiceRelativePath("MessageService")
public interface MessageService extends RemoteService {
Message getMessage(Message message);
!
}
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
21. 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
22. 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
23. 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
24. 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.
25. 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
26. Data Serialization - OnMessage
Now we have all pieces for finish the OnMessage method
@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
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
27. That’s all, happy hacking and thanks for the attention.
Full working example available on: !
https://github.com/michelefi/gwtcon-websocket
Questions?