ChannelAPI Comet Chat Demo on Google App Engine

live chat software using comet technology

A few days ago Google officially released App Engine SDK 1.4.0, which includes the Channel API. The ChannelAPI is Google’s solution to building real time Comet-enabled applications on Google’s App Engine infrastructure.

There is very little documentation at the moment regarding this new addition, so I put together a ChannelAPI demo.

The most popular comet demo has always been either the stock ticker demo or the chat demo. Considering that we are in the business of increasing sales and lowering shopping cart abandonment through chat and not financial planning, I opted to create the chat demo.

ChannelAPI Comet Chat Demo – Try it yourself

ChannelAPI Chat Demo Technical Information

To open a channel, the client page sends a request to the application server. This is called using a standard AJAX call to a servlet:

// join the room by first opening a channel on the server
joinRoom: function() {
$.ajax({ url: “/activeresponsecrm?method=createChannel”, context: document.body, dataType: “json”,
complete: function(data) {
console.info(“complete:: Channel name = ” + chatApp.removeQuotes(data.responseText));
chatApp.onJoin(chatApp.removeQuotes(data.responseText));
},
/* success: function(data) {
console.info(“success:: Channel name = ” + chatApp.removeQuotes(data));
chatApp.onJoin(chatApp.removeQuotes(data));
},*/
error: function(httpRequest, textStatus, errorThrown) {
alert(“status=” + textStatus + “,error=” + errorThrown.message);
}

});
},

Here is the servlet code that opens the channel:

public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {

String method = req.getParameter(“method”);
log.info(“method = ” + method);

if(method.equals(“createChannel”)) {
ChannelService channelService =
ChannelServiceFactory.getChannelService();

String channelId = channelService.createChannel(“default”);

log.info(“channelId = ” + channelId);

log.info(“created channel…”);

Gson gson = new Gson();
String json = gson.toJson(channelId);

resp.setContentType(“application/json”);
resp.getWriter().println(json.toString());
}

The channelId is returned in the response to the client page. The channelId key is used in the client-side code to open the socket with App Engine. The callback method invoked below demonstrates opening the socket to the App Engine Servers to initialize the comet connection.

// server created the room – now open the socket
onJoin: function(data) {
this.channelName = data;
this.channel = new goog.appengine.Channel(data);
this.socket = this.channel.open();

this.socket.onopen = function() {}
this.socket.selectors = this._selectors;
console.info(“chatMessageTemplate = ” + this.chatMessageTemplate);
this.socket.chatMessageTemplate = this.chatMessageTemplate;

// event invoked when message is received
this.socket.onmessage = this.onMessage;
},

Here is the code used to send a chat message from the client side to the server:

// send the message back to the server for processing/distribution to other clients
sendMessage: function() {

$(this['_selectors'].sendBtn).click( (function(_this) {
var text = $(_this['_selectors'].inputBox).val();
if(text == null || text == “”) { return; }
var chatMessage = _this._getChatMessage();
var json = JSON.stringify(chatMessage);
$.ajax({ url: “/activeresponsecrm”,
context: document.body,
data: “method=sendMessage&chatMessage=”+json,
dataType: “html”,
success: function(data) {
/*console.info(“message sent”);
console.info(“sel = ” + _this['_selectors'].inputBox);
$(_this['_selectors'].inputBox).val(“”);
// _this.sendLocked();*/
},
complete: function(data) {
console.info(“message sent”);
console.info(“sel = ” + _this['_selectors'].inputBox);
$(_this['_selectors'].inputBox).val(“”);
},
error: function(httpRequest, textStatus, errorThrown) {
alert(“status=” + textStatus + “,error=” + errorThrown);
}
});
//_this = null;
})(this) );

},

//private – build the ChatMessage object and return it
_getChatMessage: function() {
var chatMessage = new Object();
chatMessage.name = $(this['_selectors'].aliasBox).val();
chatMessage.message = $(this['_selectors'].inputBox).val();
chatMessage.channel = this.channelName;
console.info(“Chatmessage= ” + $(this['_selectors'].inputBox).val());
console.info(“Chatmessage= ” + chatMessage.message);
return chatMessage;

},

The server publishes the message to the channel in this servlet example:

} else if(method.equals(“sendMessage”)) {
log.info(“send a message…”);

String json = req.getParameter(“chatMessage”);
log.info(“messageJson = ” + json);

ChannelService channelService =
ChannelServiceFactory.getChannelService();
channelService.sendMessage(new
ChannelMessage(“default”,json));

log.info(“message sent…”);

resp.setContentType(“text/plain”);
resp.setStatus(200);
resp.getWriter().println(“success”);
}

On the client side, a message is received when the Google App Engine long poll returns the response to the client page, invoking the onmessage callback method defined below:

// receive message and update user-interface
onMessage:
function(evt) {
console.info(“evt = ” +evt);
console.info(“evt data = ” + evt.data);
var o = JSON.parse(evt.data);
if(!this.selectors || !this.selectors.messages) { console.error(“required selector messages is missing or selectors object not found”); return ; }

console.debug(“selector = ” + this.selectors.messages);
if(!this.chatMessageTemplate) { console.error(“chatMessageTemplate is missing or incorrectly configured”); return ; }
console.debug(this.chatMessageTemplate);

// instantiate the message div from the template
var messageElement = this.chatMessageTemplate.clone();

if(!messageElement.find(‘.text’)) {
console.warn(“warning – text element not found, using message container as text element.”);
messageElement.html(o.message);
$(this['selectors'].messages).append(messageElement);
} else {
console.debug(this['selectors'].text);
console.debug(messageElement.find(this['selectors'].text));
messageElement.find(this['selectors'].text).html(o.message);
messageElement.find(this['selectors'].timestamp).html(“TIME”);
messageElement.find(this['selectors'].name).html(o.name);

}

// paste the message HTML into the document
$(this['selectors'].messages).append(messageElement);

// scroll the bar back to the bottom
$(this['selectors'].messages).animate({scrollTop: $(this['selectors'].messages)[0].scrollHeight});

},

The Bayeux protocol publish/subscribe model is used here to facilitate real time communication just as it has done in other implementations of Comet, such as Dojo’s Cometd. Additionally, since the WebSocket specification follows the same publish/subscribe methodology, hopefully it won’t be long before Google updates the ChannelAPI to use WebSockets in HTML5 compatible browsers while supporting Comet as a fallback in older browsers.

Real-time web applications are the future of application delivery. The days of boxed software and asking users to install your software are long gone. Technologies like Comet and the ChannelAPI will enable developers to build rich, Internet applications that are 100% browser based, require zero plug-ins, and require no software to install on the local machine.

air jordan 23
mbt outlet
basket jordan femme
cheap ray ban sunglasses
ray ban outlet
louboutin soldes
hollister online shop deutschland
ray ban outlet
hollister soldes
sac lancel pas cher
louboutin femme
mulberry handbags
michael kors outlet
basket jordan femme
fake ray ban
mbt outlet
cheap jordan shoes
jordan retro 6
karen millen outlet
hollister

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>