Atmosphear: Building Asynchronous Collaborative Web Applications
Transcript of Atmosphear: Building Asynchronous Collaborative Web Applications
<Insert Picture Here>
Comet Everywhere: Building Asynchronous Collaborative Web ApplicationsLee Chuk MunnStaff [email protected]
Agenda• Introduction to Comet• Build Asynchronous Applications using
– Bayeux/Cometd• Demo: Slideshow Application using Cometd
– Grizzly Comet Framework• Demo: Tic-Tac-Toe game
• Future, Summary and Resources
Web 2.0 – Driven by Participation• Documents on the web are generated by users
• “Information age” → “Participation age”• Browser is becoming the platform to program to
Asynchronous Web Revolution• Ajax (as we know!) allows user to send requests
asynchronously without waiting for response– User Initiated– Ajax Poll
• However, the first-generation Ajax is not “asynchronous” enough– It handles a single user client-side asynchronicity
• Full asynchronicity includes “updates pushed from server to the clients” any time – Send users notifications– Allow users to communicate and collaborate within the web
application• Called “Comet”, “Server-side Push”, “Ajax Push”, or
“Reverse Ajax”
Server-mediated Collaboration
Server
Client 1 Client 2, 3, ..n
External Application
UserAction
Push
User initiated
Server-mediated Collaboration
Server
Client 1 Client 2, 3, ..n
External Application
Push Push
Application initiated
What is Comet (Ajax Push)?• A programming technique that enables web servers to
send data to the client without having any need for the client to request for it
• Allows creation of highly responsive, event-driven web applications – Keep clients up-to-date with data arriving or changing on the
server, without frequent polling• Advantages
– Lower latency, not dependent on polling frequency– Server and network do not have to deal with frequent polling
requests to check for updates
Comet Examples• GMail and GTalk • Meebo• 4homemedia.com
(using GlassFish project's Comet)• JotLive• KnowNow• Many more …
Ajax Poll vs Ajax Push• Ajax Poll:
– Send a request to the server every X seconds – The response is “empty” if there is no update
• Ajax Push– Long Poll: (most popular)
• Client sends a request to the server, server waits for an event to happen, then sends the response
• The response is never empty– Http Streaming:
• Client sends a request, server waits for events, stream multi-part/chunked responses, and then wait for the events
• The response is continually appended to
How Push works• Always “keep a connection open”
– do not respond to the initiating request until event occurs• Deliver data over a previously opened connection• Streaming is an option
– send response in multiple parts without closing the connection in between
Server Side: Servlet Thread Catastrophe
• For each client connection, a thread is assigned and it remains blocked till response is sent
• Using blocking, synchronous technology will result in a blocked thread for each open connection that is “waiting”– Every blocked thread will consume memory– This lowers scalability and can affect performance
• Servlet 2.5 are an example of blocking, synchronous technology
Technology Solution• Use technology that supports Asynchronous Request
Processing (ARP)– Release the original request thread while waiting for an event– May process the event/response on another thread than the
original request• Use new I/O (NIO) non-blocking sockets to avoid
blocking a thread per connection• Advantages
– Number of clients is primarily limited by the number of open sockets a platform can support
– Could have all clients (e.g. 10’000) “waiting” without any threads processing or blocked
Grizzly • A generic NIO framework for building scalable server
application.– Supports blocking and non blocking socket operations, over
plain or ssl connection using Java NIO API.– Enables greater performance and scalability – Uses a thread pool system and keeps the state of requests
• so that it can keep requests alive without holding a single thread for each of them.
• The GlassFish server includes the Grizzly HTTP Engine– which enables asynchronous request processing (ARP) by
avoiding blocking connections.
GlassFish• Supports 2 different implementations of Comet
– Bayeux Protocol — Often referred to as Cometd.
• Uses a publish/subscribe model for server/client communication.
• The Grizzly implementation of Cometd consists of a servlet that you reference from your web application.
– Grizzly Comet — Based on ARP, • includes a set of APIs that you use from a web component
to enable Comet functionality in your web application. • Grizzly Comet is specific to the GlassFish Server.
Grizzly Comet Components
Grizzly Comet FrameworkGrizzly Comet Framework
Grizzly HTTP Grizzly HTTP
Grizzly NIO FrameworkGrizzly NIO Framework
GrizzletGrizzlet BayeuxBayeux Messages BusMessages BusContinuationContinuation
Agenda• Web 2.0• Introduction to Comet• Asynchronous Applications using
– Bayeux/Cometd• Demo: Slideshow Application using Cometd
– Grizzly Comet Framework• Demo: Tic-Tac-Toe game
• Future, Summary and Resources
What Is Bayeux Protocol?• JSON-based protocol
– Allows clients to register interest in events and– Servers to deliver them to all the registered clients
• Based on publish/subscribe model– You subscribe a channel to receive messages– You publish a message onto a channel
• Message router (event router on the server) routes the message to anybody who subscribed the channel
• Supports multiple “channels” on a single HTTP connection– Each channel represents a separate conversation – Each channel has a unique name
• /my/channel-name– Solves the two HTTP connection limit on the browser
Message Format in Bayeux Protocol• Message format is in the format of JSON
{ "channel": "/some/name", "clientId": "83js73jsh29sjd92", "data": { "myapp" : "specific data", value: 100 } }
• JSON Messages are published on specified channels• The "/meta/*" channel is reserved for communications
with the event router itself– Channel operations:
• connect, subscribe, unsubscribe, etc.
What Is Cometd?• Bayeux protocol implementation (from Dojo toolkit)• Easy programming model
– Client • consists of JavaScript technology (DOJO toolkit) or • Java libraries that implement Bayeux
– Server • event router on the server side• The Server uses async techniques to “park” the request
without blocked threads• No server-side applications need to be created
– The server acts as a message reflector– Mediates the messages to the right channel
Software and Configuration• Glassfish Application Server (V2 or V3)
– Enable Comet Support• Configure using GlassFish V2 admin console• Configure in NetBeans 6.1+ for GlassFish V3• or Command line
– asadmin set server.http-service.http-listener.http-listener-1.property.cometSupport=true
• or Edit domain.xml in GlasFish– <http-listener id="http-listener-1" port="8080"><property
name="cometSupport" value="true" />– Grizzly Comet APIs
• A browser Client, running Javascript, with Ajax support
Configure Cometd Servlet in web.xml• The Grizzly implementation of Cometd server consists
of a servlet that you reference from your web application.
• Configure web.xml
<servlet> <servlet-name>Grizzly Cometd Servlet</servlet-name> <servlet-class> com.sun.grizzly.cometd.servlet.CometdServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>Grizzly Cometd Servlet</servlet-name> <url-pattern>/cometd/*</url-pattern> </servlet-mapping>
Step 1 : Initialize (Connect to Cometd Server)
dojo.require("dojox.cometd");
// Initialize a connection to the given Cometd server:// the GlassFish Grizzly Bayeux servletdojox.cometd.init("cometd");
Makes a call to http://myapplication/cometd
Note: /cometd/* is the url-pattern for the Grizzly Cometd Servlet configured in the web.xml for the application
Handshaking• /meta/handshake channel - request message
[{"version":"1.0","minimumVersion":"0.9","channel":"/meta/handshake","id":"0","supportedConnectionTypes":["long-polling","long-polling-json-encoded","callback-polling"]}]
• /meta/handshake channel - response message
[{"channel":"/meta/handshake","version":"1.0","supportedConnectionTypes":["long-polling","callback-polling"],"minimumVersion":"0.9","id":"0","clientId":"tgjWhMBIg5U2dPTwZBmlgw==","successful":true,"advice":{"reconnect":"retry","interval":0,"multiple-clients":false},"authSuccessful":true}]
Connect• /meta/connect channel - request message
[{"channel":"/meta/connect","clientId":"tgjWhMBIg5U2dPTwZBmlgw==","connectionType":"long-polling","id":"1"}]
• /meta/connect channel - response message
[{"channel":"/meta/connect","clientId":"tgjWhMBIg5U2dPTwZBmlgw==","successful":true,"id":"1","advice":{"reconnect":"retry","interval":0,"multiple-clients":false},"timestamp":"Tue, 01 Dec 2009 14:59:01 GMT"}]
Step 2 : Subscribe a Channel// Subscribe a channel with callback function.// Every time a message is published, // the callback function (of all clients who subscribed // the channel) gets invoked.dojox.cometd.subscribe(“channel”
, "remote topic", "callbackFunction");
Subscribe• /meta/subscribe channel - request message
[{"channel":"/meta/subscribe","subscription":"/chat/slideshow","clientId":"tgjWhMBIg5U2dPTwZBmlgw==","id":"2"},{"data":{"user":"Abhi","text":" has joined"},"channel":"/chat/slideshow","clientId":"tgjWhMBIg5U2dPTwZBmlgw==","id":"3"}]
• /meta/subscribe channel - response message
[{"channel":"/meta/subscribe","successful":true,"clientId":"tgjWhMBIg5U2dPTwZBmlgw==","subscription":"/chat/slideshow","id":"2"}, {"channel":"/chat/slideshow","successful":true,"clientId":"tgjWhMBIg5U2dPTwZBmlgw==","id":"3"}, {"channel":"/chat/slideshow","data":{"text":" has joined","user":"Abhi"},"id":"3","clientId":"tgjWhMBIg5U2dPTwZBmlgw=="}]
Step 3 : Publish data onto the channel
// Publish data (in JSON format) onto the channel.dojox.cometd.publish("channel",
{jsonname: jsonvalue});Comet Server
publish datae.g. new slide url
dojox.cometd.publish("channel", {slide: url});
Data• data channel - request message
[{"data":{"slide":"images/image0.jpg"}, "channel":"/chat/slideshow","clientId":"tgjWhMBIg5U2dPTwZBmlgw==","id":"5"}]
• data channel - response message
[{"channel":"/chat/slideshow","successful":true,"clientId":"tgjWhMBIg5U2dPTwZBmlgw==","id":"5"}]
Step 4 : Write callback function
Client 2 Client 3
// The callback function gets invoked when the client // receives a “published” message from a Cometd // server. Typically you update the page contents.callbackHandler: function(msg) { alert("msg.data.testMessage = "
+ msg.data.testMessage)}
Update BrowsercallBackFunction(slideUrl){ slide.innerHTML ="<img src='" + slideUrl + "'/>"; ...}
Step 5 : Unsubscribe, Disconnect
// Unsubscribe the channeldojox.cometd.unsubscribe(“channel”, myObject,
"callbackHandler");
// Disconnect from the Cometd serverdojox.cometd.disconnect();
Agenda• Web 2.0• Introduction to Comet• Truly Asynchronous Applications using
– Bayeux/Cometd• Demo: Slideshow Application using Cometd
– Grizzly Comet Framework• Demo: Tic-Tac-Toe game
• Future, Summary and Resources
Demo:Demo:Tic-Tac-ToeTic-Tac-Toe
(http://weblogs.java.net/blog/driscoll/archive/2008/05/comet_tictactoe_1.html)(http://weblogs.java.net/blog/driscoll/archive/2008/05/comet_tictactoe_1.html)
Grizzly Comet Framework• The Framework contains the classes required to add
support for Comet in a Web Application– Servlets, JSP, JSF etc.
• CometEngine– Main class that allows Comet support on top of Grizzly
Asynchronous Request Processing mechanism. – This class is the entry point to any component interested to
execute Comet request style.• Components can be Servlets, JSP, JSF or pure Java
class.
Grizzly Comet Framework• CometContext
– A shareable “space” applications can subscribe and get updated when the context is updated.
• A distribution mechanism for pushing messages that are delivered to multiple subscribers called CometHandler
• A la JMS Queue/Topic where CometHandlers register for information
– All http response registered to a CometContext automatically become suspended, waiting for an event (a push) to happen
• Contains references to all suspended connections (encapsulated inside a CometHandler)
– A browser receives only those messages published after the client suspends its response via CometContext
Grizzly Comet Framework (cont.)• CometHandler
– represents/encapsulates a suspended connection (or response)
– can be resumed later when an event happens– has methods that are invoked by the Container:
• When a push operation happens• When I/O operations are ready to be processed
(asynchronous read or write)• When the browser closes the connection
• CometEvent– An object containing the state of the Comet Context
• content updated, client connected/disconnected, etc.
Steps for Implementing Comet • Step 1: Initialize Comet
– Register a CometContext• Step 2: Define your CometHandler
– To handle various events• Step 3: Connect to Comet Servlet
– Add CometHandler to CometContext– Connection gets suspended as response not committed
• Step 4: Advertise changes– Client - Sends a message– Server - Notify all other clients
• Step 5: More details on Client– Update Client
Step 1: Initialize CometRegister a CometContext
//In TTTComet Servlet class...public void init(ServletConfig config) { ServletContext context = config.getServletContext();
contextPath = context.getContextPath() + "/TTTComet1";
// Comet Entry Point CometEngine engine = CometEngine.getEngine(); CometContext cometContext = engine.register(contextPath); cometContext.setExpirationDelay(120 * 1000);
Step 2: Define a CometHandler• The CometHandler<E> interface methods
//Invoked when CometContext.notify() is calledpublic void onEvent(CometEvent ce);
// Invoked when the browser closes a suspended// connection or when the suspend timeout expire
public void onInterrupt(CometEvent ce);
// Invoked when the request is suspendedpublic void onInitialize(CometEvent ce);
// Attach an object.. most probably the HttpServletResponsepublic void attach(E e);
CometHandler: TTTHandler//Inside the TTTComet Servlet class...private class TTTHandler implements
CometHandler<HttpServletResponse> {private HttpServletResponse response;
//When push operation happens, //onEvent is invoked after CometContext.notify() is called public void onEvent(CometEvent event) throws IOException {
if (CometEvent.NOTIFY == event.getType()) {//Add your business logic here
…// resume all suspended connections event.getCometContext().resumeCometHandler(this);
}}...
Step 3: Add CometHandler to CometContext• Connect to server with GET, GET is now suspended
– Instead of POST, recommended by Comet
protected void doGet(HttpServletRequest request,, HttpServletResponse response)
throws ServletException, IOException {
TTTHandler handler = new TTTHandler();handler.attach(response);CometEngine engine = CometEngine.getEngine();CometContext context =
engine.getCometContext(contextPath);context.addCometHandler(handler);
}
Step 4: Advertise ChangesPost is called (client)<table><tr><td id="cell0"><img id="img0" src="resources/0.gif"
onclick=postMe("0")></td>...</table>
var url = "TTTComet1";function postMe(arg) {
var xhReq = createXMLHttpRequest();xhReq.open("POST", url, true);xhReq.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");xhReq.send("cell="+arg);
};
Step 4: Advertise ChangesPost is called (server)
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String cellStr = request.getParameter("cell");PrintWriter writer = response.getWriter();…game.turn(cell);… CometEngine engine = CometEngine.getEngine();CometContext<?> context =
engine.getCometContext(contextPath);context.notify(null); //Notify all Handlers
CometHandler: TTTHandler (revisit)//Inside the TTTComet Servlet class...private class TTTHandler implements CometHandler<HttpServletResponse> {
private HttpServletResponse response;
//When push operation happens, //onEvent is invoked after CometContext.notify() is called
public void onEvent(CometEvent event) throws IOException {if (CometEvent.NOTIFY == event.getType()) {
PrintWriter writer = response.getWriter();writer.write("<script type='text/javascript'>parent.chImg("
+ game.getJSON() + ")</script>\n"); writer.flush();event.getCometContext().resumeCometHandler(this);
}}…
Sample JSON output
{ “win”: “-1”, “board”: [“0”, “1”, “10”,
“0”, “1”, “10”,“1”, “10, “0” ],
“turn”: “10” }
Step 5: Client Side update (called from Server Side onEvent() in CometHandler)
function chImg(args) {var data = eval(args);// redraw the boardfor (i = 0; i < 9; i++) {
document.getElementById("img"+i).src ="resources/"+data.board[i]+".gif";
} // -1 is unfinished, 0 is tie, 1 is X win, 2 is O winvar statusMsg; if (data.win == 0) {
statusMsg = "It's a tie!";} else if (data.win == 1) {
… document.getElementById("gstatus").innerHTML = statusMsg;// restart the pollhidden.location = url; //GET request that gets suspended
Comet Implementations in other servers• Tomcat 6
– CometProcessor• event(CometEvent event)• event.close()
• Jetty– Continuation
• continuation.suspend();• continuation.resume()
• Weblogic– AbstractAsyncServlet
• doRequest(..) and doResponse(..)• notify(..)
● Suspend..● Event.. wake .. ● Resume
Servlet 3.0• Defined by JSR-315 Expert Group
– DWR, Jetty, Tomcat, GlassFish project, and ICEfaces participants
• Standard asynchronous processing API being defined– Asynchronous I/O– Suspendible requests
• Suspend a response: HttpServletRequest.startAsync()• Resume a response: AsyncContext.complete()
• Will improve portability – of DWR, Cometd, ICEfaces etc
• Servlet 3.0 will supports only a subset of Comet
Project Atmosphere• Comet techniques aren't standardized among Web
Container– Grizzly Comet applications aren't portable (cannot run on
Tomcat nor Jetty)• Servlet 3.0 supports only subset of Comet• Project Atmosphere – portable Comet framework
– a POJO based framework using Inversion of Control (IoC) to bring Comet to the masses
• Suspend: @Suspend• Resume: @Resume
– A framework which can run on any Java based Web Server – can run on Servlet 2.5 compliant Web Server
– Can detect Servlet 3.0 too
Summary• The Asynchronous Web will revolutionize human
interaction• Push can scale with Asynchronous Request
Processing• Writing Games is not that complicated• With GlassFish project and Project Grizzly, the
revolution begins with your application today!• Try Atmosphere and Servlet 3.0
<Insert Picture Here>
Comet Everywhere: Building Asynchronous Collaborative Web ApplicationsLee Chuk MunnStaff [email protected]