Core Java

Real-time Applications with AngularJS and Java – Part 2

1. Introduction

As the title of this article implies, this is the second part of how to create a real-time application using AngularJS and Java. The first part showed how to automatically refresh a page content using the periodic refresh AJAX design pattern. Here, I will show and explain the concept behind long polling.

If you have not, I would suggest that you read and try the example of part 1. I will use the same skeleton application and modify it, it’s important that you know the basic concepts of the part 1 as I will not explain them again here.

Moreover, a basic knowledge of AngularJS and Spring is important as I will not explain how to set your workspace up, nor will I explain how they interact with each other.

angularjs_small

AngularJS Programming Cookbook

In this ebook, we provide a compilation of AngularJS based examples that will help you kick-start your own web projects. We cover a wide range of topics, from Single Page Apps and Routing, to Data Binding and JSON Fetching. With our straightforward tutorials, you will be able to get your own projects up and running in minimum time. Download the cookbook by joining the Web Code Geeks Newsletter.

2. Long Polling

Long polling is a concept used to emulate the server push (CometD, Bayeux, Atmosphere, WebSocket, etc.). Basically, the client starts an XMLHttpRequest with the server using Ajax. The server then accepts the request and check for updated information to send to the client. If the server does not find any new data, it loops until it finds or until a fixed amount of time to avoid infinite loops or client connection timeout.

Long polling
Figure 1. Long polling in action

At the time of writing this article, Facebook uses Long Polling to update the UI with new information. Using Google Chrome or any new browser’s network analyzer, you can see it in action. Go to your Facebook home page and hit F12. Go to the network tab and filter to show only XHR. You’ll see that a request is sent to the server through a specific pull channel and stays in the Pending state for a little while, then the request is completed, a new one is started and so on.

Facebook Example
Figure 2. Facebook’s long polling

The main advantage of this method vs the periodic refresh pattern is that we reduce quite a lot the number of requests sent to the server. On the other hand, this uses and holds a thread from the server’s thread pool which could potentially run out of free threads. That means a user would get locked out of the system until a thread is freed, but this is not a show stopper if the server is properly configured or if you have load balancing on different instances.

3. The RESTful JSON Java Back-end

3.1. The new Task object status

As I said in the introduction, I will modify the example of part 1 in which the Task object had a duration that was decremented by a thread every second or so. That meant the data was actually changing quite often, so the periodic refresh was a good solution to display those changes to the client. We simply set the refresh rate at 1 second and it appeared to be real-time. Regarding the Long Polling, it would not make much sense to have the data updated that often. What we want to emulate is the server telling the client: “Hold on, I will send you data once I got something new for you.“. The data has to be unpredictably updated to see the long polling in action. To implement that, I will add a new TaskStatus that a Task can be in that is CREATED.

TaskStatus.java

public enum TaskStatus {
  CREATED,
  IDLE,
  RUNNING,
  SUCCESS;
}

3.2. The Task object

The new version of the Task object need to be instantiated with the new status by default, meaning that all new Tasks will be created with the CREATED status.

Task.java

public class Task {
  private TaskStatus status = TaskStatus.CREATED;
  private long duration;

  public TaskStatus getStatus() {
    return status;
  }

  public void setStatus(TaskStatus status) {
    this.status = status;
  }

  public long getDuration() {
    return duration;
  }

  public void setDuration(long duration) {
    this.duration = duration;
  }

  public void decrementDuration() {
    this.duration--;
  }

  public boolean isRunning() {
    return this.status.equals(TaskStatus.RUNNING);
  }
  
  public String getName() {
    return this.toString();
  }

  public void start() {
    this.status = TaskStatus.RUNNING;
  }
}

3.3. The TaskCreator

To emulate users creating new Tasks, I created a TaskCreator object that randomly creates a new Task with the status CREATED. The point is that, unlike the previous example of part 1, I will query only for new information instead of the whole thing. That obviously will reduce the amount of data transferred over the network.

TaskCreator.java

@Component
@Scope("singleton")
public class TaskCreator {
  private static final int MAX_TASK_DURATION = 5000;
  private static final int MAX_TASK_CREATION_INTERVAL = 10000;
  private static final Random RANDOMIZER = new Random();
  
  @Autowired
  private TaskExecutor executor;

  public void start() {
    
    Runnable taskPoolConsumer = () -> {
      synchronized (executor) {
        while (true) {
          try {
            Task newTask = new Task();
            
            newTask.setStatus(TaskStatus.CREATED);
            newTask.setDuration(RANDOMIZER.nextInt(MAX_TASK_DURATION));
            this.executor.addTask(newTask);
  
            this.executor.wait(RANDOMIZER.nextInt(MAX_TASK_CREATION_INTERVAL));
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    };
    
    new Thread(taskPoolConsumer).start();
  }
}

3.4. The TaskExecutor

As I said above, we want to improve the application so it only returns Task objects that have changed. A Task will be considered as changed if it’s either new or if its status has changed since the last time it was queried. For simplicity’s sake, this example will work only for one user. You could, like Facebook does, have a channel opened for each user and compute the delta between what is in the UI and what is in the back-end. To compute the delta in this example, I will simply keep a second list of Task in which will be added Task that were started or completed. This deals pretty badly with concurrency, but again, for simplicity’s sake, I decided that this was enough to show the concept.

TaskExecutor.java

@Component
@Scope("singleton")
public class TaskExecutor {
  private List pool = new LinkedList<>();
  private Set updatedTaskPool = new HashSet<>();
  
  @PostConstruct
  public void initialize() {
    Runnable taskPoolConsumer = () -> {
      synchronized(this) {
        while (true) {
          try {
            this.pool.stream()
                .filter(task -> task.isRunning() && task.getDuration() > 0)
                .forEach(task -> {
                  task.decrementDuration();
                });
            
            this.pool.stream()
              .filter(task -> task.isRunning() && task.getDuration() == 0)
              .forEach(task -> {
                task.setStatus(TaskStatus.SUCCESS);
                this.updatedTaskPool.add(task);
              });
  
            this.wait(1000);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    };
    
    new Thread(taskPoolConsumer).start();
    
  }
  
  public synchronized List getUpdatedTasks() {
    List updatedTasks = new LinkedList<>();
    
    updatedTasks.addAll(this.pool.stream()
        .filter(task -> task.getStatus().equals(TaskStatus.CREATED))
        .collect(Collectors.toList()));
    updatedTasks.addAll(this.updatedTaskPool);
    
    this.changeCreatedStatusToIdle();
    this.updatedTaskPool.clear();
    
    return updatedTasks;
  }

  private void changeCreatedStatusToIdle() {
    this.pool.stream()
        .filter(task -> task.getStatus().equals(TaskStatus.CREATED))
        .forEach(task -> task.setStatus(TaskStatus.IDLE));
  }

  
  public synchronized void startAllTasks() throws InterruptedException {
    this.pool.stream()
      .filter(task -> task.getStatus().equals(TaskStatus.IDLE))
      .forEach(task -> {
        task.start();
        this.updatedTaskPool.add(task);
      });  
  }

  public List getPool() {
    this.changeCreatedStatusToIdle();
    return this.pool;
  }

  public void addTask(Task taskToAdd) {
    this.pool.add(taskToAdd);
  }
}

3.5. TaskService

In our TaskService, we want to inject the new TaskCreator singleton and start it at the initialization. Then, we want to create a new mapping for our RestController that is to make the distinction between the function that returns all Task and the one that returns only updated information. That last one will implement the loop necessary for long polling.

TaskService.java

@RestController
@RequestMapping("/api/task")
public class TaskService {
  @Autowired
  private TaskExecutor taskExecutor;
  @Autowired
  private TaskCreator taskCreator;
  
  @PostConstruct
  public void initialize() {
    this.taskCreator.start();
  }
  
  @RequestMapping(path = "/all", method = RequestMethod.GET)
  public List getTasks() {
    return this.taskExecutor.getPool();
  }
  
  @RequestMapping(method = RequestMethod.GET)
  public List getUpdatedTasks() {
    List updatedTasks = null;
    
    // Fetch updated task until there is one or more
    do {
      updatedTasks = this.taskExecutor.getUpdatedTasks();
    } while (updatedTasks.size() == 0);
    
    return updatedTasks;
  }
  
  @RequestMapping(method = RequestMethod.POST)
  public void addTask(@RequestBody Task taskToAdd) {
    this.taskExecutor.addTask(taskToAdd);
  }
  
  public void startIdleTasks() throws InterruptedException {
    this.taskExecutor.startAllTasks();
  }
}

As you can see, I did not implement the loop break condition on a maximum waiting time. You could also add a Thread.sleep() to reduce the number of calls to getUpdatedTasks() of the TaskExecutor if necessary.

4. Front-end implementation with AngularJS

The front-end part also changes a little bit. First, we want to separate the function that returns all Tasks and the function that returns only the updated Tasks. That last one will be a recursive function calling itself when data has arrived through the channel or if the server replies with an error message. Then we either push the Task received in the Array of Tasks if the status is IDLE as the TaskExecutor changes status from CREATED to IDLE before sending them to the client or we try and find the existing Task to update its status if the status is different from IDLE (either RUNNING or SUCCESS).

index.xhtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"  
    xmlns:h="http://java.sun.com/jsf/html"  
    xmlns:f="http://java.sun.com/jsf/core">
    
  <h:head>
    <title>Real-time applications - Part 1 - Java Code Geeks</title>
    <link rel="stylesheet" href="https://examples.javacodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS8=bootstrap/3.3.5/css/bootstrap.min.css"/>
    <script src="https://examples.javacodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/angular.js/1.4.5/angular.min.js"></script>
    
    <script>
      var part1 = angular.module("part1", []);
      part1.controller("RealtimeCtrl", function($scope, $http, $timeout) {
        
        $scope.addTask = function() {
          $http.post("api/task", $scope.task);
        }
        
        $scope.getTasks = function() {
          $http.get("api/task/all")
            .success(function(data) {
              $scope.tasks = data;
            });
        }
        
        $scope.getUpdatedTasks = function() {
          $http.get("api/task")
            .success(function(data) {
              data.forEach(function(currentTask) {
                if (currentTask.status === 'IDLE') {
                  $scope.tasks.push(currentTask);
                } else {
                  $scope.tasks.forEach(function(taskToBeUpdated) {
                    if (taskToBeUpdated.name === currentTask.name) {
                      taskToBeUpdated.status = currentTask.status;
                      taskToBeUpdated.running = currentTask.status === 'RUNNING';
                    }
                  });
                }
              });
              
              // Recursive of Long Polling on success.
              $scope.getUpdatedTasks();
            }).error(function() {
              // Recursive of Long Polling on error.
              $scope.getUpdatedTasks();
            });
        }
        
        $scope.activateRealtime = function() {
          $scope.getUpdatedTasks();
        }
        
        $scope.getTasks();
      });
      
    </script>
  </h:head>
    
  <h:body>
    <div ng-app="part1" ng-controller="RealtimeCtrl" class="container">
      <h1>Real-time application <SMALL>part 2</SMALL></h1>
      <h2>Add task</h2>
      <h:form>
        <label for="durationField">Duration (in seconds):</label>
        <input type="number" id="durationField" class="form-control" ng-model="task.duration"/>
        <button type="button" ng-click="addTask()" class="btn btn-success">Add task</button>
        <button type="button" ng-click="getTasks()" class="btn btn-default">Refresh Tasks</button>
        <button type="button" ng-click="activateRealtime()" class="btn btn-default">Activate Auto Refresh</button>
        <h:commandButton actionListener="#{taskController.startTasks}" 
            styleClass="btn btn-default"
            value="Start Idle Tasks">
          <f:ajax execute="@form"/>
        </h:commandButton>
      </h:form>
      
      <h2>Listing</h2>
      <ul class="list-group">
        <li ng-repeat="curTask in tasks" class="list-group-item {{curTask.running ? 'active' : ''}}">
          {{curTask.name}} ({{curTask.status}})<span class="badge">{{curTask.duration}}</span>
        </li>
      </ul>
    </div>
  </h:body>    
</html>

5. What’s next?

As you can see, it’s a little bit more complex to implement in comparison with the periodic refresh AJAX pattern, but we get a better feel of real-time. As the back-end loops and hangs the thread for a couple of seconds until it has found new data, the notification of the update seems to come from the server in real-time. Now, the above example is not the perfect implementation of long polling. It has many flaws compared to Facebook’s implementation, but for demonstration purposes, I think it does the job.

In the next part of this article, I will show you the new HTML 5 WebSocket and how this same application here can be improved to get the Task through a socket opened with the server.

6. Download the Eclipse project

This was an example of how to integrate AngularJS and Spring MVC to create an application that is updated automatically using Long Polling.

Download
You can download the full source code of this example here: Long Polling

Sylvain Cloutier

Sylvain has been programming in Java for the past 5 years, mainly in the aerospace industry, as a lead developer of complex web based aircraft wiring systems. He is also one of the organizers of the Java User Group of Quebec City and currently works as a Java developer consultant at CGI.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Yusuf Indah
Yusuf Indah
3 years ago

Simply encouraging, though I have not run this appli, but I want to go further

Back to top button