Platinum Partner
java,news,html5,jersey,servlet 3,java ee 7,chat application,asynchronous repsonse,jax-rs 2,longpolling

Jax-RS 2 and LongPolling based Chat Application

LongPolling; 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..

jax-rs-async

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. WhileAsyncResponse 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/

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}