11. Introducing the Demo
The Software
The Server:
GlassFish 4 (promoted build B88), Oracle
The server-side Java app can generate random price quotes
The Client:
HTML5 Ext JS framework, Sencha
The Browser: Chrome , Google
Charles Proxy
The Goal
1. The server generates random stock prices for a 12-stock portfolio
2. The user sends a request for price quote for a stock
3. The HTML client displays the received price
4. Observe what went over the network
12. The Server: Rest
@Path("stock")
public
class
RestResource
{
@GET
@Produces(value
=
MediaType.APPLICATION_JSON)
@Path("/{ticker}")
public
String
getRandomValue(@PathParam(value
=
"ticker")
String
ticker)
{
return
new
Gson().toJson(RandomStocksGenerator.getDataForTicker(ticker));
}
}
13. The Client: AJAX in Ext JS
'mypanel
button[action=doRestCall]':
click:
this.onRestCall
onRestCall:
function(btn)
{
var
ticker
=
Ext.ComponentQuery.query('mypanel
textfield[name=ticker]')
[0].getValue();
var
rest_url
=
"http://"
+
document.location.host
+
document.location.pathname
+
"rest/stock/";
rest_url
=
rest_url
+
ticker;
Ext.Ajax.request({
url:
rest_url,
scope:
this,
success:
function(response)
{
var
a
=
Ext.JSON.decode(response.responseText);
a.price
=
parseFloat(a.price).toFixed(4);
console.log(a);
}
});
}
15. What’s SSE
• It’s not a request-response mode
• The browser subscribes to events from server
by creating EventSource pointing to this server
• The server can send data to client at any time
• The browser receives an event + data
16. Browser Subscribes to SSE
To listen to any messages:
source.onmessage
=
function(e)
{….};
var
source
=
new
EventSource('http://localhost:8080/stock/events');
source.addEventListener('open',
function(e)
{
//
Connection
was
opened.
},
false);
source.addEventListener('priceChanged',
function(e)
{
var
priceQuote
=
JSON.parse(e.data);
//…
},
false);
source.addEventListener('error',
function(e)
{
if
(e.readyState
==
EventSource.CLOSED)
{
//
Connection
was
closed.
}
},
false);
17. Pushing SSE from Server
The server sends events as text messages with MIME
text/event-‐stream
Each message starts with data: and end with a pair /n/n:
'data:
{"price":
"123.45"}/n/n'
The browser concatenates all these messages separating
them with /n.
18. Pushing from Glassfish (Jersey)
@Path("stock")
public
class
SseResource
{
private
static
final
SseBroadcaster
BROADCASTER
=
new
SseBroadcaster();
private
boolean
isRunning
=
false;
private
Timer
broadcastTimer;
@GET
@Path("stock-‐generator")
@Produces(SseFeature.SERVER_SENT_EVENTS)
public
EventOutput
itemEvents()
{
final
EventOutput
eventOutput
=
new
EventOutput();
BROADCASTER.add(eventOutput);
if
(!isRunning)
startBroadcastTask();
return
eventOutput;
}
}
19. Broadcasting in Jersey
protected
void
startBroadcastTask()
{
broadcastTimer
=
new
Timer();
broadcastTimer
.schedule(new
SseBroadcastTask(BROADCASTER,
0),
0,
300);
this.isRunning
=
true;
}
public
class
SseBroadcastTask
extends
TimerTask
{
private
final
SseBroadcaster
owner;
public
SseBroadcastTask(SseBroadcaster
owner,
int
timeout)
{
this.owner
=
owner;
}
@Override
public
void
run()
{
OutboundEvent
event
=
new
OutboundEvent.Builder().data(
String.class,
RandomStocksGenerator.getRandomValues().toJson()).build();
owner.broadcast(event);
21. Introducing the SSE Demo
The Software
The Server:
GlassFish 4 (promoted build B88), Oracle
The Java app can generate random price quotes
The Client:
HTML5 Ext JS framework, Sencha
The Chrome Browser, Google
The Goal
1. The server generates random stock prices for a 12-stock portfolio
2. The server pushes 3 price quotes per second using SSE
3. The HTML client shows the prices in a data grid
23. WebSocket
• Standard W3C protocol (RFC6455)
• Java EE 7 includes WebSocket API (JSR-356)
• Web Browsers include window.WebSocket
object. No plugins required.
24. How WebSocket Works
Establish a socket connection via HTTP for the initial handshake.
Switch the protocol from HTTP to a socket-based protocol.
Send messages in both directions simultaneously.
This is not a request-response model!
25. Protocol Upgrade: HTTP à WebSocket
• Client sends Upgrade HTTP-request
• Server confirms Upgrade
• Client receives Upgrade response from server
• Client changes WebSocket.readyState to open
29. WebSocket Endpoint
@ServerEndpoint(value
=
"/stock-‐generator",
encoders
=
{
StockMessageEncoder.class
},
decoders
=
{
StockMessageDecoder.class
})
public
class
StocksEndpoint
{
@OnOpen
public
void
onOpen(Session
session)
{
…
}
@OnMessage
public
void
onMessage(StockMessage
message,
Session
client)
{
}
@OnClose
public
void
onClose(Session
session)
{…
…
}
…
}
30. public
class
StockMessageDecoder
implements
Decoder.Text<StockMessage>
{
@Override
public
StockMessage
decode(String
s)
throws
DecodeException
{
Gson
gson
=
new
Gson();
return
gson.fromJson(s,
StockMessage.class);
}
}
public
class
StockMessageEncoder
implements
Encoder.Text<StockMessage>
{
@Override
public
String
encode(StockMessage
object)
throws
EncodeException
{
return
new
Gson().toJson(object);
}
}
Decoder
Encoder
31. Heartbeats: Pings and Pongs
Heartbeats is a mechanism to
check that connection is still alive.
If a WebSocket implementation
receives a ping it has
to respond with a pong ASAP.
There is no JavaScript API to support Pings and Pongs.
33. The Software
The Server:
GlassFish 4 (promoted build B88), Oracle
The Java app can generate random price quotes
The Client:
HTML5 Ext JS framework, Sencha
The Chrome Browser, Google
Charles proxy
Introducing the WebSocket Demo
The Goal
1. The server generates random stock prices for a 12-stock portfolio
2. The server pushes 20 price quotes per second using WebSocket
3. The HTML client shows the prices in a data grid
34. What was Pushed via WebSocket
The size of each data push:
The length price quote data: from 42 to 45 bytes
WebSocket added overhead: 2 bytes
{"symbol":
"APPL",
"id":
555,
"price":
"451.29"}
35. Comparing Overheads
Http Request-Response
Each roundtrip has:
request header: 429 bytes + 268 bytes response header
+ 46 bytes data
Server-Sent Events
Initial request header 406 bytes + 268 bytes response
header
Each push: 46 bytes data + 8 bytes message wrapper
WebSocket
Initial upgrade request: 406 bytes + 268 bytes
Each push 46 bytes data + 2 bytes message wrapper
It’s full-duplex, not HTTP-based, works much faster.
37. Reducing kilobytes of data to 2 bytes...
and reducing latency from 150ms to 50 ms
is far more than marginal.
In fact, these two factors alone are enough
to make WebSocket seriously interesting to
Google.
Ian Hickson, Google
The lead HTML5 writer
38. Where to use WebSockets
- Live trading/sports ticker
- Controlling medical equipment over the web
- Chat applications
- Multiplayer online games
- Real-time updating social streams
39. Unedited drafts of the book Enterprise Web Development are published at
enterprisewebbook.com
40. Contact Info
Farata Systems: faratasystems.com
Personal blog: yakovfain.com
Twitter: @yfain
Podcasts in Russian: americhka.us
Thank you!