Using Asynchronous Servlets for Web Push Notifications

Overview

    Purpose

    This tutorial will show you how to implement Asynchronous Servlets in a simple "shout-out" Web Application with real-time updates.

    Time to Complete

    Approximately 45 minutes

    Introduction

    In Java EE 6, Asynchronous Servlets can be used to optimize resources in Web Applications. Asynchronous Servlets allow you to handle the request and write the response in a different thread to reduce idle time or processing on the server's thread pool. In a common Web Application, a thread is used per request. When a request comes to the Web Server, it assigns a thread to handle that request and return a response. When the Servlet ends processing the thread is freed and reused for another request.

    There is a problem with this behavior; if a request  takes long to complete then the thread may spend a long time to be freed. This is particularly problematic in two scenarios:

    • There is a long process to complete in order to calculate a response.
    • There is a streaming response or one that is waiting for some external data and in the meantime is in a waiting state.

    Asynchronous Servlets solve the two scenarios by deferring the response and allowing another thread to write to it.

    In this tutorial you will create a simple Web Application that uses Asyncrhonous Servlets and Long Polling for Web Push Notifications.

    You can find more about the different Web Server Push techniques as well as the different uses of Asynchronous Servlets in the Summary of this tutorial.

    Software Requirements

    The following is a list of software requirements:

    Prerequisites

    Before starting this tutorial, you should:

    • Have knowledge of the Java 7 Programming Language.
    • Have basic knowledge of the Java EE 6 Platform, specifically Servlets and JSP.
    • Have basic HTML and Javascript knowledge.
    • This tutorial uses Java 7 syntax. In the code examples that requires change in code syntax if used with previous versions, refer to: http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html for changes in the Java 7 Platform.

Creating a simple Java EE6 Web Application

    For this tutorial you will create a simple Web Application first.

    Open NetBeans.

    Go to: File > New Project

    Select Java Web > Web Application and click Next.

    Type a name for your Web Application and Click Next.

    From the server list select Oracle WebLogic Server.

    Select Java EE 6 Web from the Java EE Version dropdown list,

    Click Next.

    Leave all options unchecked in the Frameworks pane.

    Click Finish and your application project will be created and configured.

    You have created a simple Web Application from NetBeans using Java EE 6.

Verify Web Application project configuration

    To ensure you have the proper settings, verify the project configuration.

    Click File > Project Properties (Your project name).

    In the Sources pane, select JDK 7 for the Source/Binary Format.

    Code samples in this tutorial uses Java 7 syntax

    In the Run pane, verify the Java EE Version is Java EE 6 Web. The server dictates the Java EE Version setting. Selecting Oracle WebLogic Server sets the Java EE Version to Java EE 6 Web.

    If you are running other Server ensure that the Java EE Version is 6.

    Make sure that Deploy on Save is checked. This ensures that every change you make in the source code is deployed to the Web Server, and you can see your changes in real time.

Create the JSP view and Shout Servlet.

    To begin you need a page that will invoke the Servlet services.

    Open the project and open the index.jsp file located under Web Pages if it's not already open.

    Add the highlighted code.

    index.jsp - Html layout.

       
      <%@page contentType="text/html" pageEncoding="UTF-8"%>
      <!DOCTYPE html>
      <html>
          <head>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
              <title>JSP Page</title>
          </head>
          <body>
              <h1>SHOUT-OUT!</h1>
              <form method="POST" action="shoutServlet">
                  <table>
                      <tr>
                          <td>Your name:</td>
                          <td><input type="text" id="name" name="name"/></td>
                      </tr>
                      <tr>
                          <td>Your shout:</td>
                          <td><input type="text" id="message" name="message" /></td>
                      </tr>
                      <tr>
                          <td><input type="submit" value="SHOUT" /></td>
                      </tr>
                  </table>
              </form>
              <h2> Current Shouts </h2>
              <div id="content">
                  <% if (application.getAttribute("messages") != null) {%>
                  <%= application.getAttribute("messages")%>
                  <% }%>
              </div>
          </body>
      </html>
      

    To create the Servlet, right-click on Source Packages and select New > Java Class.

    Name the class ShoutServlet and type com.example.shout in the Package field.

    Start with a simple implementation of the Shout Servlet that is synchronous and stores all the shouts in the Servlet Context by adding the highlighted code:

    ShoutServlet.java - Simple Servlet implementation.

       
      package com.example.shout;
      
      import java.io.IOException;
      import javax.servlet.ServletContext;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet(urlPatterns = {"/shoutServlet"})
      public class ShoutServlet extends HttpServlet {
      
          @Override
          protected void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
              String name = request.getParameter("name");
              String message = request.getParameter("message");
              String htmlMessage = "<p><b>" + name + "</b><br/>" + message + "</p>";
              ServletContext sc = request.getServletContext();
              if (sc.getAttribute("messages") == null) {
                  sc.setAttribute("messages", htmlMessage);
              } else {
                  String currentMessages = (String) sc.getAttribute("messages");
                  sc.setAttribute("messages", htmlMessage + currentMessages);
              }
              response.sendRedirect("index.jsp");
          }
      }
      

    To test the application, right-click on the project and select Run.

    A browser will open.

    Fill the form with your name and type a message as Your shout. Click SHOUT.

    You should see your shout posted in the browser.

    Open another browser window and move it beside the first.

    Post another shout by filling the form.

    Notice only one browser updates.

    In the next step you will learn how to use basic ajax code to update the page using an Asynchronous Servlet.

Add the Asynchronous Servlet and JavaScript code.

    You will create a simple implementation of Long Polling to notify the browser of changes.

    Open the index.jsp file.

    Add the AJAX call to wait until a message is posted and print it on the page by adding the highlighted code.

    index.jsp - Using AJAX for get method on ShoutServlet.

      <%@page contentType="text/html" pageEncoding="UTF-8"%>
      <!DOCTYPE html>
      <html>
          <head>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
              <title>JSP Page</title>
          </head>
          <body>
              <h1>SHOUT-OUT!</h1>
              <form method="POST" action="shoutServlet">
                  <table>
                      <tr>
                          <td>Your name:</td>
                          <td><input type="text" id="name" name="name"/></td>
                      </tr>
                      <tr>
                          <td>Your shout:</td>
                          <td><input type="text" id="message" name="message" /></td>
                      </tr>
                      <tr>
                          <td><input type="submit" value="SHOUT" /></td>
                      </tr>
                  </table>
              </form>
              <h2> Current Shouts </h2>
              <div id="content">
                  <% if (application.getAttribute("messages") != null) {%>
                  <%= application.getAttribute("messages")%>
                  <% }%>
              </div>
              <script>
                  var messagesWaiting = false;
                  function getMessages(){
                      if(!messagesWaiting){
                          messagesWaiting = true;
                          var xmlhttp = new XMLHttpRequest();
                          xmlhttp.onreadystatechange=function(){
                              if (xmlhttp.readyState==4 && xmlhttp.status==200) {
                                  messagesWaiting = false;
                                  var contentElement = document.getElementById("content");
                                  contentElement.innerHTML = xmlhttp.responseText + contentElement.innerHTML;
                              }
                          }
                          xmlhttp.open("GET", "shoutServlet?t="+new Date(), true);
                          xmlhttp.send();
                      }
                  }
                  setInterval(getMessages, 1000);
              </script>
          </body>
      </html>
      

    The code you added will continuously request the Shout Servlet for a new message and append the response of the Servlet to the content div.

    You need to add the asynchronous support to the Servlet and implement the message notification.

    Open the ShoutServlet class and add the highlighted code.

    ShoutServlet.java - Asynchronous Servlet for notifications.

      package com.example.shout;
      
      import java.io.IOException;
      import java.io.PrintWriter;
      import java.util.ArrayList;
      import java.util.LinkedList;
      import java.util.List;
      import javax.servlet.AsyncContext;
      import javax.servlet.ServletContext;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet(urlPatterns = {"/shoutServlet"}, asyncSupported=true)
      public class ShoutServlet extends HttpServlet {
          private List<AsyncContext> contexts = new LinkedList<>();
      
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
              final AsyncContext asyncContext = request.startAsync(request, response);
              asyncContext.setTimeout(10 * 60 * 1000);
              contexts.add(asyncContext);
          }
      
          @Override
          protected void doPost(HttpServletRequest request, HttpServletResponse response)
                  throws ServletException, IOException {
              List<AsyncContext> asyncContexts = new ArrayList<>(this.contexts);
              this.contexts.clear();
              String name = request.getParameter("name");
              String message = request.getParameter("message");
              String htmlMessage = "<p><b>" + name + "</b><br/>" + message + "</p>";
              ServletContext sc = request.getServletContext();
              if (sc.getAttribute("messages") == null) {
                  sc.setAttribute("messages", htmlMessage);
              } else {
                  String currentMessages = (String) sc.getAttribute("messages");
                  sc.setAttribute("messages", htmlMessage + currentMessages);
              }
              for (AsyncContext asyncContext : asyncContexts) {
                  try (PrintWriter writer = asyncContext.getResponse().getWriter()) {
                      writer.println(htmlMessage);
                      writer.flush();
                      asyncContext.complete();
                  } catch (Exception ex) {
                  }
              }
      
          }
      }
      

    When the Servlet receives a "get" Request, it starts an AsyncContext by calling: request.startAsync(request, response). This will notify the Web Container that at the end of the request call it should free the handling thread and leave the connection open so that other thread writes the response and end the connection.

    The next thing you did in the code is adding the AsyncContext to a List so we can get all waiting contexts in the doPost method.

    Then you modified the doPost method to get a safe copy of the list of AsyncContext.Then you cleared the common list to prevent a pending request to be notified twice. Finally for all the AsyncContexts queued you wrote the message to their responses and completed those requests by calling asyncContext.complete()

    Test your application by running it.

    Right-click on your project name and Select Run.

    A browser will open. Open another browser window and move them side by side.

    Fill the form with your name and type a message as Your shout. Click SHOUT.

    You should see your shout posted on both browser windows.

    The page still refreshes when you post a message. To make the page refresh partially when you submit a shout, add the highlighted code to the index.jsp file:

    index.jsp - AJAX on get and post methods on ShoutServlet.

      <%@page contentType="text/html" pageEncoding="UTF-8"%>
      <!DOCTYPE html>
      <html>
          <head>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
              <title>JSP Page</title>
          </head>
          <body onload="getMessages();">
              <h1>SHOUT-OUT!</h1>
              <form>
                  <table>
                      <tr>
                          <td>Your name:</td>
                          <td><input type="text" id="name" name="name"/></td>
                      </tr>
                      <tr>
                          <td>Your shout:</td>
                          <td><input type="text" id="message" name="message" /></td>
                      </tr>
                      <tr>
                          <td><input type="button" onclick="postMessage();" value="SHOUT" /></td>
                      </tr>
                  </table>
              </form>
              <h2> Current Shouts </h2>
              <div id="content">
                  <% if (application.getAttribute("messages") != null) {%>
                  <%= application.getAttribute("messages")%>
                  <% }%>
              </div>
              <script>
                  function postMessage() {
                      var xmlhttp = new XMLHttpRequest();
                      xmlhttp.open("POST", "shoutServlet?t="+new Date(), false);
                      xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                      var nameText = escape(document.getElementById("name").value);
                      var messageText = escape(document.getElementById("message").value);
                      document.getElementById("message").value = "";
                      xmlhttp.send("name="+nameText+"&message="+messageText);
                  }
                  var messagesWaiting = false;
                  function getMessages(){
                      if(!messagesWaiting){
                          messagesWaiting = true;
                          var xmlhttp = new XMLHttpRequest();
                          xmlhttp.onreadystatechange=function(){
                              if (xmlhttp.readyState==4 && xmlhttp.status==200) {
                                  messagesWaiting = false;
                                  var contentElement = document.getElementById("content");
                                  contentElement.innerHTML = xmlhttp.responseText + contentElement.innerHTML;
                              }
                          }
                          xmlhttp.open("GET", "shoutServlet?t="+new Date(), true);
                          xmlhttp.send();
                      }
                  }
                  setInterval(getMessages, 1000);
              </script>
          </body>
      </html>
      

    Your application is now complete. You can test it on your browser and see how it updates automatically across different browsers.

Summary / next steps

    In this tutorial you learned the basics of Asynchronous Servlets with a simple example that uses long polling for notifications.

    Resources

    For more information on the topics of this tutorial:

    Questions and answers:

    • How does long polling works?
      Long polling notifies the client by keeping the network connection open until there is a notification for the client at which point the response is written and the connection is closed. The client has to keep the connection open by opening a new connection every time the server responds or times out.
    • Are there any other notification mechanisms?
      Yes there are a lot of notification mechanisms besides long polling, although arguably long polling is the easiest to implement from scratch in this tutorial. There is Comet (AJAX server push) Web Sockets (for persistent full duplex connections) Use of Java Applets for server communication, Flash XML Sockets, etc.
    • What are other uses of Asynchronous Servlets?
      Asynchronous Servlets are ideal in any scenario where the response cannot be completed when a request comes in a timely manner.
      You can use Asynchronous Servlets in the following scenarios:
      • Server push (notifications) Since the response is delayed until a certain event happens.
      • Calling external services that require an outbound connection, since the handling thread will wait on a external resource thus the response is delayed until another service responds.
      • Waiting for resources like a database query result. Sometimes the application waits too long on such resources and that is iddle time of the request so you can implement asynchronous Servlets to reduce iddle time of the handling thread.
    • What is the advantage of Asynchronous Servlets.
      Using Asynchronous Servlets frees the request handling thread allowing another connection to be opened and handled, greatly optimizing server load

    Credits

    • Lead Curriculum Developer: Eduardo Moranchel







To help navigate this Oracle by Example, note the following:

Hiding Header Buttons:
Click the Title to hide the buttons in the header. To show the buttons again, simply click the Title again.
Topic List Button:
A list of all the topics. Click one of the topics to navigate to that section.
Expand/Collapse All Topics:
To show/hide all the detail for all the sections. By default, all topics are collapsed
Show/Hide All Images:
To show/hide all the screenshots. By default, all images are displayed.
Print:
To print the content. The content currently displayed or hidden will be printed.

To navigate to a particular section in this tutorial, select the topic from the list.