code: private void runJobLauncherLoop() throws JobException {
      try {
        while (true) {
          currentJobLauncher = this.jobScheduler.buildJobLauncher(jobProps);
          // in "run once" case, job scheduler will remove current job from the scheduler
          boolean isEarlyStopped = this.jobScheduler.runJob(jobProps, jobListener, currentJobLauncher);
          boolean isRetriggerEnabled = this.isRetriggeringEnabled();
          if (isEarlyStopped && isRetriggerEnabled) {
            log.info("Job {} will be re-triggered.", jobProps.getProperty(ConfigurationKeys.JOB_NAME_KEY));
          } else {
            break;
          }
          currentJobLauncher = null;
        }
      } catch (Exception e) {
        log.error("Failed to run job {}", jobProps.getProperty(ConfigurationKeys.JOB_NAME_KEY), e);
        throw new JobException("Failed to run job " + jobProps.getProperty(ConfigurationKeys.JOB_NAME_KEY), e);
      }
    }
docs: <p> In some cases, the job launcher will be early stopped.
  It can be due to the large volume of input source data.
  In such case, we need to re-launch the same job until
  the job launcher determines it is safe to stop.