@Before
  public void init() throws Exception {

    earlierExecution =
        MetaDataInstanceFactory.createJobExecutionWithStepExecutions(122L, Arrays.asList("step"));
    earlierExecution.setStatus(BatchStatus.FAILED);
    earlierExecution.setExitStatus(ExitStatus.FAILED);
    earlierExecution.setStartTime(new Date());
    earlierExecution.setEndTime(new Date(earlierExecution.getStartTime().getTime() + 100));
    assertFalse(earlierExecution.isRunning());

    jobExecution =
        MetaDataInstanceFactory.createJobExecutionWithStepExecutions(
            123L, Arrays.asList("first", "step"));
    jobExecution.setStatus(BatchStatus.COMPLETED);
    jobExecution.setExitStatus(ExitStatus.COMPLETED);
    jobExecution.setStartTime(new Date());
    jobExecution.setEndTime(new Date(earlierExecution.getEndTime().getTime() + 100));
    assertFalse(jobExecution.isRunning());

    Iterator<StepExecution> iterator = jobExecution.getStepExecutions().iterator();
    iterator.next();
    StepExecution stepExecution = iterator.next();
    stepExecution.setStatus(BatchStatus.COMPLETED);
    stepExecution.setExitStatus(ExitStatus.COMPLETED.addExitDescription("Foo"));

    metrics = new SimpleJobExecutionMetrics(jobService, "job");
  }
 @Override
 public ExitStatus afterStep(StepExecution stepExecution) {
   if (!(stepExecution.getStatus() == BatchStatus.COMPLETED)) {
     return ExitStatus.EXECUTING;
   }
   long expecting = localState.getExpecting();
   boolean timedOut;
   try {
     logger.debug("Waiting for results in step listener...");
     timedOut = !waitForResults();
     logger.debug("Finished waiting for results in step listener.");
   } catch (RuntimeException e) {
     logger.debug("Detected failure waiting for results in step listener.", e);
     stepExecution.setStatus(BatchStatus.FAILED);
     return ExitStatus.FAILED.addExitDescription(e.getClass().getName() + ": " + e.getMessage());
   }
   if (timedOut) {
     stepExecution.setStatus(BatchStatus.FAILED);
     throw new ItemStreamException("Timed out waiting for back log at end of step");
   }
   return ExitStatus.COMPLETED.addExitDescription("Waited for " + expecting + " results.");
 }
  /*
   * 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();
      }
    }
  }