Jax-RS 2 and LongPolling based Chat Application
Join the DZone community and get the full member experience.
Join For Freelongpolling ; also known as reverse ajax or comet, is a push method that operates seamlessly in javascript aided web browsers.
although advanced push techniques such as sse and websocket with html 5 are already available, either these technologies are still in the process of development or they are not supported entirely by web browsers, longpolling and similar techniques be in demand.
in longpolling technique, the web browser makes a request to the server, and this request is suspended on the server until a ready response ( resource ) is founded. the pending http request is sent back to the available source web browser when a response is obtained. the web browser transmits a new polling message as soon as it gets the http response, and this process repeats itself until a new http response is ready..
from time to time, the failure of the pending polling request originating from server, web browser or current network environment can also be a matter. in this case, web browser takes the error information from the server, sent a request once more and then the polling starts again.
in longpolling technique, xmlhttprequest object which is a javascript object can be used on the web browser side. forthcoming asyncronousresponse objects with jax-rs 2 can be used on the server side in terms of flexibility and convenience.
now, i would like to share with you a chat application instance that contains the association of longpolling technique and jax-rs 2 within java ee 7 .
dependent library
chat application can easily operate on a servlet container, but available container environment must comply with the standard servlet 3 because jax-rs 2 uses forthcoming asynchronous handlers together with servlet 3 on the container.
<dependency> <groupid>org.glassfish.jersey.containers</groupid> <artifactid>jersey-container-servlet</artifactid> <version>2.0</version> </dependency>
note:
- jersey is the reference implementer library of the standard jax-rs. http://jersey.java.net/
- jetty 9 or tomcat 7 can be used for the servlet 3 support .
entry point of the chat application (rest endpoint)
@applicationpath("app") public class app extends application { @override public set<class<?>> getclasses() { set<class<?>> sets=new hashset<>(); sets.add(chat.class); return sets; } }
the above application class type object that is wrapped with @applicationpath notation contains the entry point of the chat application. the defined chat class into the hashset is described as a rest resource belonging to the chat application.
chat resource
@path("/chat") public class chat { final static map<string, asyncresponse> waiters = new concurrenthashmap<>(); final static executorservice ex = executors.newsinglethreadexecutor(); @path("/{nick}") @get @produces("text/plain") public void hangup(@suspended asyncresponse asyncresp, @pathparam("nick") string nick) { waiters.put(nick, asyncresp); } @path("/{nick}") @post @produces("text/plain") @consumes("text/plain") public string sendmessage(final @pathparam("nick") string nick, final string message) { ex.submit(new runnable() { @override public void run() { set nicks = waiters.keyset(); for (string n : nicks) { // sends message to all, except sender if (!n.equalsignorecase(nick)) waiters.get(n).resume(nick + " said that: " + message); } } }); return "message is sent.."; } }
asyncresponse type parameter that is found as a parameter on hangup(..) method defines the longpolling response object which will be suspended on the server. while asyncresponse type objects can be provided from jax-rs environment automatically, these suspended objects can be submitted to the user (web browser) as a response at any time. the second parameter [ @pathparam("nick") string nick ] found on the hangup(..) method obtains the user nick name which is sent to the method on the url.
the information about suspended asyncresponse objects belong to which user is needed subsequently. concurrenthashmap that is a structure of a concurrent map can be used for this purpose. in this way,each nick -> asyncresponse object that is sent to thehangup(..) method will be kept under record.
the second method of the chat class sendmessage(..) can be accessed with http /post method and operates as restful method which derives chat messages from the web browser.
when users submit a chat message with the knowledge of nick name to the server, sendmessage(..) restful method obtains this information from the method parameters, and this information is being transacted as asynchronous through the executorservice object. of course a simple thread can be used instead of executorservice object that is used in here.
just as new thread( new runnable(){ … } ).start(); . the key issue here is the running of the sent message at the background and to prevent the interruption of default /post request, more precisely, to prevent the waiting of message information made push to all users.
the nick -> asyncresponse pair that is filled in the previous hangup(..) method in the concurrenthashmap is consumed into the runnable task object which is found in the sendmessage(..) method, and this chat message is transmitted to all suspended asyncresponse objects except the user itself. the resume() method included in the asyncresponse objects allows the @suspended response object to respond. that is to say, ( suspend ) before, and then ( resume ) when chat message resource become ready.
html components
<!doctype html> <html> <head> <title></title> <meta charset="utf-8"> </head> <body> <span></span> <table> <tr class="nick"> <td>nick:</td> <td><input id="nick"/></td> </tr> <tr> <td>message:</td> <td><textarea id="message" disabled="disabled"></textarea></td> </tr> </table> <button>post</button> <ul></ul> <script src="resources/jquery-1.9.1.js" type="text/javascript"></script> <script type="text/javascript" src="resources/chat.js"></script> </body> </html>
in the above html page, there are a nick input object, a text area object where the message will be entered and an html button where the message will be sent and these are lined in an html table object. the textarea is made as disabled initially because it is expected to enter the nick name firstly from the user when the application is started. when the user sends the first request (nick name), unchangeable state of this field is modified and is made message writable. also, when nick name is sent to the server (i.e. when the first polling request is made), html table row element which is a class type (
<tr>
) is hidden.
chat application is required to provide xmlhttprequest objects and http requests as asynchronous. in this application we will benefit from jquery ajax library which facilitates these operations and uses xmlhttprequest objects in the background. in addition to jquery dependence, there is a chat.js script that was written by us in the project.
chat.js
// the polling function must be called once, //it will call itself recursively in case of error or performance. var poolit = function () { $.ajax({ type: "get", url: "/app/chat/" + $("#nick").val(), // access to the hangup(..) method datatype: "text", // incoming data type text success: function (message) { // the message is added to the <li/> element when it is received. $("ul").append("<li>" + message + "</li>"); poolit(); // link to the re-polling when a message is consumed. }, error: function () { poolit(); // start re-polling if an error occurs. } }); } // when the submit button is clicked; $("button").click(function () { if ($(".nick").css("visibility") === "visible") { // if <tr> line is visible; $("textarea").prop("disabled", false); // able to enter data $(".nick").css("visibility", "hidden"); // make <tr> line invisible; $("span").html("chat started.."); // information message // polling operation must be initiated at a time poolit(); } else // if it is not the first time ; $.ajax({ type: "post", // http post request url: "/app/chat/" + $("#nick").val(),// access to the sendmessage(..) method. datatype: "text", // incoming data type -> text data: $("textarea").val(), // chat message to send contenttype: "text/plain", // the type of the sent message success: function (data) { $("span").html(data); // it writes [message is sent..] if successful. // blink effect $("span").fadeout('fast', function () { $(this).fadein('fast'); }); } }); });
testing the application
because the jetty 9.0.0.rc2 plugin is included in the pom.xml configuration file of the application, the application can be easily run with
> mvn jetty:run-war
command. the application can be accessed at
http://localhost:8080/
after above goal run.
you can access source code and live example follow the next url => http://en.kodcu.com/2013/06/jax-rs-2-and-longpolling-based-chat-application/
Opinions expressed by DZone contributors are their own.
Comments