How to Stop a REST Java Thread: Angular, Spring MVC & MongoDB

I had a REST method that is launched from an Angular web by pushing a button. It crawls and access some portal APIs depending on given keywords, this search can take time to complete so I wanted another button that could stop this search from the same web. It also logs the start and stop date and time in a Mongo database to keep the user informed what button has been clicked or if the search is still running. It also registers the finished reason: Whether it has an error, finished normally, or interrupted by the user who pushed the stop button.

In Angular:
  1. In the controller:
  2.     var canceller = $q.defer();
        $scope.startSearch = function () {
            $scope.executing = true;
            articlesCommunicationService.launchSearch(canceller).then(function (data) {
                $scope.executing = false;
            }).catch(function (response) {
                $scope.executing = false;
            });
       };
    
        $scope.stopSearch = function () {
            canceller.resolve("user cancelled");
            canceller = $q.defer();
            articlesCommunicationService.stopSearch().then(function (data) {
                $scope.executing = false;
            }).catch(function () {
                $scope.error = 'Error stopping the search';
            });
        };
    
  3. In the service
  4.             var launchSearch = function (canceller) {
                    return $http.get(url_search + "/start", 
                              {timeout: canceller.promise}).then(function (response) {
                        return response.data;
                    });
                };
    
In Java:
  1. The Stop Search REST call reads form the DB the thread id of the thread process that is running, it compares this id with all the set of threads that are running under this container. If the thread exists it means the search is still running, and sends a signal to interrupt it. It writes in MongoDB the time and the reason for the end of the search.
  2.  public void stopSearch() throws Exception {
    
            Log portalLog = logRepository.findFirstByOrderByStartDesc();
    
            Set setOfThread = Thread.getAllStackTraces().keySet();
    
            for (Thread thread : setOfThread) {
                if (thread.getId() == portalLog.getThreadId()) {
                    portalLog.setStopDate();
                    portalLog.setCause("Interrupted by user");
                    logRepository.save(portalLog);
                    thread.interrupt();
                }
    
            }
        }
    
    
    I had to write in MongoDB in the stop process and not in the start process when the interrupted signal is catched because once the signal is thrown Mongo is not able to access the database due to the following exception:
    org.springframework.data.mongodb.UncategorizedMongoDbException: 
    Interrupted acquiring a permit to retrieve an item from the pool ;
    
  3. The Start Seach REST call needs to catch the interrupt signal exception, we can do this in 2 ways: Either sleep the process or ask if the process has been interrupted. This make sense because we don't want the process to be interrupted at any time, leaving the database or the computations in a unstable state. Hence add the following code in a part of your code where it is safe to be finished:
  4.  if (Thread.currentThread().isInterrupted()) {
         throw new InterruptedException();
     }
    
    or/and
       Thread.sleep(2 * 1000);
    
    Also remember to write in the database the process id you may want to interrupt so you can access to it from a different thread:
       Log portalLog = new Log();
       portalLog.setStartDate();
       portalLog.setThreadId(Thread.currentThread().getId());
       log.debug("Thread id: " + Thread.currentThread().getId());
       logRepository.save(portalLog);
    
    Then you can catch the exception if you like, but this is not needed:
       } catch (InterruptedException ex) { 
          //Interrupted exception is not able to write in mongo
          log.warn("The user has interrupted the search");
       }
    

Comments