/**
   * @param jobIdentifier a job execution id or job name
   * @param minStatus the highest status to exclude from the result
   * @return
   */
  private List<JobExecution> getJobExecutionsWithStatusGreaterThan(
      String jobIdentifier, BatchStatus minStatus) {

    Long executionId = getLongIdentifier(jobIdentifier);
    if (executionId != null) {
      JobExecution jobExecution = jobExplorer.getJobExecution(executionId);
      if (jobExecution.getStatus().isGreaterThan(minStatus)) {
        return Arrays.asList(jobExecution);
      }
      return Collections.emptyList();
    }

    int start = 0;
    int count = 100;
    List<JobExecution> executions = new ArrayList<JobExecution>();
    List<JobInstance> lastInstances = jobExplorer.getJobInstances(jobIdentifier, start, count);

    while (!lastInstances.isEmpty()) {

      for (JobInstance jobInstance : lastInstances) {
        List<JobExecution> jobExecutions = jobExplorer.getJobExecutions(jobInstance);
        if (jobExecutions == null || jobExecutions.isEmpty()) {
          continue;
        }
        for (JobExecution jobExecution : jobExecutions) {
          if (jobExecution.getStatus().isGreaterThan(minStatus)) {
            executions.add(jobExecution);
          }
        }
      }

      start += count;
      lastInstances = jobExplorer.getJobInstances(jobIdentifier, start, count);
    }

    return executions;
  }
  /*
   * Start a job by obtaining a combined classpath using the job launcher and
   * job paths. If a JobLocator has been set, then use it to obtain an actual
   * job, if not ask the context for it.
   */
  public int start(String moduleNm, String jobIdentifier, String[] parameters, Set<String> opts)
      throws Exception {

    INaviModuleContext context = null;

    try {
      context = NaviModuleContextFactory.getInstance().getNaviModuleContext(moduleNm);
      launcher = (JobLauncher) context.getBean("jobLauncher");
      jobExplorer = (JobExplorer) context.getBean("jobExplorer");
      jobRepository = (JobRepository) context.getBean("jobRepository");

      Assert.state(
          launcher != null,
          "A JobLauncher must be provided.  Please add one to the configuration.");
      if (opts.contains("-restart") || opts.contains("-next")) {
        Assert.state(
            jobExplorer != null,
            "A JobExplorer must be provided for a restart or start next operation.  Please add one to the configuration.");
      }

      String jobName = moduleNm + "_" + jobIdentifier;

      JobParameters jobParameters =
          jobParametersConverter.getJobParameters(
              StringUtils.splitArrayElementsIntoProperties(parameters, "="));
      Assert.isTrue(
          parameters == null || parameters.length == 0 || !jobParameters.isEmpty(),
          "Invalid JobParameters "
              + Arrays.asList(parameters)
              + ". If parameters are provided they should be in the form name=value (no whitespace).");

      if (opts.contains("-stop")) {
        List<JobExecution> jobExecutions = getRunningJobExecutions(jobName);
        if (jobExecutions == null) {
          throw new JobExecutionNotRunningException(
              "No running execution found for job=" + jobName);
        }
        for (JobExecution jobExecution : jobExecutions) {
          jobExecution.setStatus(BatchStatus.STOPPING);
          jobRepository.update(jobExecution);
        }
        return exitCodeMapper.intValue(ExitStatus.COMPLETED.getExitCode());
      }

      if (opts.contains("-abandon")) {
        List<JobExecution> jobExecutions = getStoppedJobExecutions(jobName);
        if (jobExecutions == null) {
          throw new JobExecutionNotStoppedException(
              "No stopped execution found for job=" + jobName);
        }
        for (JobExecution jobExecution : jobExecutions) {
          jobExecution.setStatus(BatchStatus.ABANDONED);
          jobRepository.update(jobExecution);
        }
        return exitCodeMapper.intValue(ExitStatus.COMPLETED.getExitCode());
      }

      if (opts.contains("-restart")) {
        JobExecution jobExecution = getLastFailedJobExecution(jobName);
        if (jobExecution == null) {
          throw new JobExecutionNotFailedException(
              "No failed or stopped execution found for job=" + jobName);
        }
        jobParameters = jobExecution.getJobInstance().getJobParameters();
        jobName = jobExecution.getJobInstance().getJobName();
      }

      Job job;
      if (jobLocator != null) {
        job = jobLocator.getJob(jobIdentifier);
      } else {
        job = (Job) context.getBean(jobIdentifier);
        AbstractJob tmptJob = (AbstractJob) job;
        // 重写jobNm
        tmptJob.setName(jobName);
      }

      if (opts.contains("-next")) {
        JobParameters nextParameters = getNextJobParameters(job);
        Map<String, JobParameter> map =
            new HashMap<String, JobParameter>(nextParameters.getParameters());
        map.putAll(jobParameters.getParameters());
        jobParameters = new JobParameters(map);
      }

      JobExecution jobExecution = launcher.run(job, jobParameters);
      return exitCodeMapper.intValue(jobExecution.getExitStatus().getExitCode());

    } catch (Throwable e) {
      String message = "Job Terminated in error: " + e.getMessage();
      log.error(message, e);
      NaviDaemonJobRunner.message = message;
      return exitCodeMapper.intValue(ExitStatus.FAILED.getExitCode());
    } finally {
      if (context != null) {
        context.close();
      }
    }
  }