@Test
  public void testUnexpectedRollback() throws Exception {

    taskletStep.setTransactionManager(
        new ResourcelessTransactionManager() {
          @Override
          protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
            super.doRollback(status);
            throw new UnexpectedRollbackException("bar");
          }
        });

    taskletStep.setTasklet(
        new Tasklet() {

          public RepeatStatus execute(StepContribution contribution, ChunkContext attributes)
              throws Exception {
            attributes
                .getStepContext()
                .getStepExecution()
                .getExecutionContext()
                .putString("foo", "bar");
            return RepeatStatus.FINISHED;
          }
        });

    taskletStep.execute(stepExecution);
    assertEquals(FAILED, stepExecution.getStatus());
    Throwable e = stepExecution.getFailureExceptions().get(0);
    assertEquals("bar", e.getMessage());
    assertEquals(0, stepExecution.getCommitCount());
    assertEquals(1, stepExecution.getRollbackCount()); // Failed transaction counts as rollback
    assertEquals(0, stepExecution.getExecutionContext().size());
  }
 @Test
 public void testInterrupted() throws Exception {
   taskletStep.setStepExecutionListeners(new StepExecutionListener[] {new InterruptionListener()});
   taskletStep.execute(stepExecution);
   assertEquals(STOPPED, stepExecution.getStatus());
   assertEquals(STOPPED.toString(), stepExecution.getExitStatus().getExitCode());
 }
  @Test
  public void testApplicationException() throws Exception {

    taskletStep.execute(stepExecution);
    assertEquals(FAILED, stepExecution.getStatus());
    assertEquals(FAILED.toString(), stepExecution.getExitStatus().getExitCode());
  }
  @Test
  public void testAfterStepFailureWhenTaskletSucceeds() throws Exception {

    final RuntimeException exception = new RuntimeException();
    taskletStep.setStepExecutionListeners(
        new StepExecutionListenerSupport[] {
          new StepExecutionListenerSupport() {
            @Override
            public ExitStatus afterStep(StepExecution stepExecution) {
              throw exception;
            }
          }
        });
    taskletStep.setTasklet(
        new Tasklet() {

          public RepeatStatus execute(StepContribution contribution, ChunkContext attributes)
              throws Exception {
            return RepeatStatus.FINISHED;
          }
        });
    taskletStep.execute(stepExecution);
    assertEquals(COMPLETED, stepExecution.getStatus());
    assertFalse(stepExecution.getFailureExceptions().contains(exception));
    assertEquals(3, jobRepository.getUpdateCount());
  }
 private List<Object[]> buildStepExecutionParameters(StepExecution stepExecution) {
   Assert.isNull(
       stepExecution.getId(),
       "to-be-saved (not updated) StepExecution can't already have an id assigned");
   Assert.isNull(
       stepExecution.getVersion(),
       "to-be-saved (not updated) StepExecution can't already have a version assigned");
   validateStepExecution(stepExecution);
   stepExecution.setId(stepExecutionIncrementer.nextLongValue());
   stepExecution.incrementVersion(); // Should be 0
   List<Object[]> parameters = new ArrayList<Object[]>();
   String exitDescription =
       truncateExitDescription(stepExecution.getExitStatus().getExitDescription());
   Object[] parameterValues =
       new Object[] {
         stepExecution.getId(),
         stepExecution.getVersion(),
         stepExecution.getStepName(),
         stepExecution.getJobExecutionId(),
         stepExecution.getStartTime(),
         stepExecution.getEndTime(),
         stepExecution.getStatus().toString(),
         stepExecution.getCommitCount(),
         stepExecution.getReadCount(),
         stepExecution.getFilterCount(),
         stepExecution.getWriteCount(),
         stepExecution.getExitStatus().getExitCode(),
         exitDescription,
         stepExecution.getReadSkipCount(),
         stepExecution.getWriteSkipCount(),
         stepExecution.getProcessSkipCount(),
         stepExecution.getRollbackCount(),
         stepExecution.getLastUpdated()
       };
   Integer[] parameterTypes =
       new Integer[] {
         Types.BIGINT,
         Types.INTEGER,
         Types.VARCHAR,
         Types.BIGINT,
         Types.TIMESTAMP,
         Types.TIMESTAMP,
         Types.VARCHAR,
         Types.INTEGER,
         Types.INTEGER,
         Types.INTEGER,
         Types.INTEGER,
         Types.VARCHAR,
         Types.VARCHAR,
         Types.INTEGER,
         Types.INTEGER,
         Types.INTEGER,
         Types.INTEGER,
         Types.TIMESTAMP
       };
   parameters.add(0, Arrays.copyOf(parameterValues, parameterValues.length));
   parameters.add(1, Arrays.copyOf(parameterTypes, parameterTypes.length));
   return parameters;
 }
  @Test
  public void testDefaultFailure() throws Exception {

    JobExecution jobExecution = createJobExecution();
    job.execute(jobExecution);
    assertEquals(2, stepNamesList.size());
    assertTrue(stepNamesList.contains("s1"));
    assertTrue(stepNamesList.contains("fail"));

    assertEquals(BatchStatus.FAILED, jobExecution.getStatus());
    assertEquals(ExitStatus.FAILED.getExitCode(), jobExecution.getExitStatus().getExitCode());

    StepExecution stepExecution1 = getStepExecution(jobExecution, "s1");
    assertEquals(BatchStatus.COMPLETED, stepExecution1.getStatus());
    assertEquals(ExitStatus.COMPLETED, stepExecution1.getExitStatus());

    StepExecution stepExecution2 = getStepExecution(jobExecution, "fail");
    assertEquals(BatchStatus.FAILED, stepExecution2.getStatus());
    assertEquals(ExitStatus.FAILED.getExitCode(), stepExecution2.getExitStatus().getExitCode());
  }
  @Test
  @Ignore // FIXME
  public void testTransactionException() throws Exception {

    final SkipWriterStub<String> writer = new SkipWriterStub<String>();
    FaultTolerantStepFactoryBean<String, String> factory =
        new FaultTolerantStepFactoryBean<String, String>();
    factory.setItemWriter(writer);

    @SuppressWarnings("serial")
    DataSourceTransactionManager transactionManager =
        new DataSourceTransactionManager(dataSource) {
          private boolean failed = false;

          @Override
          protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
            if (writer.getWritten().isEmpty()
                || failed
                || !isExistingTransaction(status.getTransaction())) {
              super.doCommit(status);
              return;
            }
            failed = true;
            status.setRollbackOnly();
            super.doRollback(status);
            throw new UnexpectedRollbackException("Planned");
          }
        };

    factory.setBeanName("stepName");
    factory.setTransactionManager(transactionManager);
    factory.setCommitInterval(2);

    ItemReader<String> reader = new ListItemReader<String>(Arrays.asList("1", "2"));
    factory.setItemReader(reader);

    JobRepositoryFactoryBean repositoryFactory = new JobRepositoryFactoryBean();
    repositoryFactory.setDataSource(dataSource);
    repositoryFactory.setTransactionManager(transactionManager);
    repositoryFactory.afterPropertiesSet();
    JobRepository repository = repositoryFactory.getObject();
    factory.setJobRepository(repository);

    JobExecution jobExecution = repository.createJobExecution("job", new JobParameters());
    StepExecution stepExecution = jobExecution.createStepExecution(factory.getName());
    repository.add(stepExecution);

    Step step = factory.getObject();

    step.execute(stepExecution);
    assertEquals(BatchStatus.FAILED, stepExecution.getStatus());

    assertEquals("[]", writer.getCommitted().toString());
  }
 @Test
 public void testExecuteFailure() throws Exception {
   step.setJob(
       new JobSupport("child") {
         @Override
         public void execute(JobExecution execution) throws UnexpectedJobExecutionException {
           execution.setStatus(BatchStatus.FAILED);
           execution.setEndTime(new Date());
         }
       });
   step.afterPropertiesSet();
   step.execute(stepExecution);
   assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
 }
 @Test
 public void testInterruptedWithCustomStatus() throws Exception {
   taskletStep.setTasklet(
       new Tasklet() {
         public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
             throws Exception {
           contribution.setExitStatus(new ExitStatus("FUNNY"));
           throw new JobInterruptedException("Planned");
         }
       });
   taskletStep.execute(stepExecution);
   assertEquals(STOPPED, stepExecution.getStatus());
   assertEquals("FUNNY", stepExecution.getExitStatus().getExitCode());
 }
 @Test
 public void testExecuteException() throws Exception {
   step.setJob(
       new JobSupport("child") {
         @Override
         public void execute(JobExecution execution) throws UnexpectedJobExecutionException {
           throw new RuntimeException("FOO");
         }
       });
   step.afterPropertiesSet();
   step.execute(stepExecution);
   assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
   assertEquals("FOO", stepExecution.getFailureExceptions().get(0).getMessage());
 }
  @Test
  public void testOpenFailure() throws Exception {
    final RuntimeException exception = new RuntimeException();
    taskletStep.setStreams(
        new ItemStream[] {
          new ItemStreamSupport() {
            @Override
            public void open(ExecutionContext executionContext) throws ItemStreamException {
              throw exception;
            }
          }
        });

    taskletStep.execute(stepExecution);
    assertEquals(FAILED, stepExecution.getStatus());
    assertTrue(stepExecution.getFailureExceptions().contains(exception));
    assertEquals(2, jobRepository.getUpdateCount());
  }
  @Test
  public void testBeforeStepFailure() throws Exception {

    final RuntimeException exception = new RuntimeException();
    taskletStep.setStepExecutionListeners(
        new StepExecutionListenerSupport[] {
          new StepExecutionListenerSupport() {
            @Override
            public void beforeStep(StepExecution stepExecution) {
              throw exception;
            }
          }
        });
    taskletStep.execute(stepExecution);
    assertEquals(FAILED, stepExecution.getStatus());
    assertTrue(stepExecution.getFailureExceptions().contains(exception));
    assertEquals(2, jobRepository.getUpdateCount());
  }
  @Test
  public void testRepositoryErrorOnFailure() throws Exception {

    taskletStep.setTasklet(
        new Tasklet() {

          public RepeatStatus execute(StepContribution contribution, ChunkContext attributes)
              throws Exception {
            throw new RuntimeException("Tasklet exception");
          }
        });

    jobRepository.setFailOnUpdateExecutionContext(true);
    taskletStep.execute(stepExecution);
    assertEquals(UNKNOWN, stepExecution.getStatus());
    Throwable e = stepExecution.getFailureExceptions().get(0);
    assertEquals("Expected exception in step execution context persistence", e.getMessage());
  }
  @Test
  public void testDecisionUnmappedExitStatus() throws Exception {
    ApplicationContext context =
        new GenericXmlApplicationContext(
            "classpath:/org/springframework/batch/core/jsr/step/DecisionStepTests-decisionInvalidExitStatus-context.xml");

    JobLauncher launcher = context.getBean(JobLauncher.class);
    Job job = context.getBean(Job.class);

    JobExecution execution = launcher.run(job, new JobParameters());
    assertEquals(BatchStatus.COMPLETED, execution.getStatus());
    assertEquals(2, execution.getStepExecutions().size());

    for (org.springframework.batch.core.StepExecution curExecution :
        execution.getStepExecutions()) {
      assertEquals(BatchStatus.COMPLETED, curExecution.getStatus());
    }
  }
  @Test
  public void testRepositoryErrorOnUpdateStepExecution() throws Exception {

    taskletStep.setTasklet(
        new Tasklet() {

          public RepeatStatus execute(StepContribution contribution, ChunkContext attributes)
              throws Exception {
            return RepeatStatus.FINISHED;
          }
        });

    jobRepository.setFailOnUpdateStepExecution(1);
    taskletStep.execute(stepExecution);
    assertEquals(UNKNOWN, stepExecution.getStatus());
    Throwable e = stepExecution.getFailureExceptions().get(0);
    assertEquals("JobRepository failure forcing exit with unknown status", e.getMessage());
  }
 /**
  * Test method for {@link
  * org.springframework.batch.core.step.AbstractStep#execute(org.springframework.batch.core.StepExecution)}
  * .
  */
 @Test
 public void testExecuteSunnyDay() throws Exception {
   step.setJob(
       new JobSupport("child") {
         @Override
         public void execute(JobExecution execution) throws UnexpectedJobExecutionException {
           execution.setStatus(BatchStatus.COMPLETED);
           execution.setEndTime(new Date());
         }
       });
   step.afterPropertiesSet();
   step.execute(stepExecution);
   assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
   assertTrue(
       "Missing job parameters in execution context: " + stepExecution.getExecutionContext(),
       stepExecution
           .getExecutionContext()
           .containsKey(JobStep.class.getName() + ".JOB_PARAMETERS"));
 }
 @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.");
 }
  @Test
  /*
   * Exception in afterStep is ignored (only logged).
   */
  public void testAfterStepFailureWhenTaskletFails() throws Exception {

    final RuntimeException exception = new RuntimeException();
    taskletStep.setStepExecutionListeners(
        new StepExecutionListenerSupport[] {
          new StepExecutionListenerSupport() {
            @Override
            public ExitStatus afterStep(StepExecution stepExecution) {
              throw exception;
            }
          }
        });
    taskletStep.execute(stepExecution);
    assertEquals(FAILED, stepExecution.getStatus());
    assertTrue(stepExecution.getFailureExceptions().contains(taskletException));
    assertFalse(stepExecution.getFailureExceptions().contains(exception));
    assertEquals(2, jobRepository.getUpdateCount());
  }
  @Test
  public void testUpdateError() throws Exception {

    final RuntimeException exception = new RuntimeException();
    taskletStep.setJobRepository(
        new UpdateCountingJobRepository() {
          boolean firstCall = true;

          @Override
          public void update(StepExecution arg0) {
            if (firstCall) {
              firstCall = false;
              return;
            }
            throw exception;
          }
        });

    taskletStep.execute(stepExecution);
    assertEquals(UNKNOWN, stepExecution.getStatus());
    assertTrue(stepExecution.getFailureExceptions().contains(exception));
    assertTrue(stepExecution.getFailureExceptions().contains(taskletException));
  }
 /**
  * Validate StepExecution. At a minimum, JobId, StartTime, and Status cannot be null. EndTime can
  * be null for an unfinished job.
  *
  * @throws IllegalArgumentException
  */
 private void validateStepExecution(StepExecution stepExecution) {
   Assert.notNull(stepExecution);
   Assert.notNull(stepExecution.getStepName(), "StepExecution step name cannot be null.");
   Assert.notNull(stepExecution.getStartTime(), "StepExecution start time cannot be null.");
   Assert.notNull(stepExecution.getStatus(), "StepExecution status cannot be null.");
 }
  @Override
  public void updateStepExecution(StepExecution stepExecution) {

    validateStepExecution(stepExecution);
    Assert.notNull(
        stepExecution.getId(),
        "StepExecution Id cannot be null. StepExecution must saved" + " before it can be updated.");

    // Do not check for existence of step execution considering
    // it is saved at every commit point.

    String exitDescription =
        truncateExitDescription(stepExecution.getExitStatus().getExitDescription());

    // Attempt to prevent concurrent modification errors by blocking here if
    // someone is already trying to do it.
    synchronized (stepExecution) {
      Integer version = stepExecution.getVersion() + 1;
      Object[] parameters =
          new Object[] {
            stepExecution.getStartTime(),
            stepExecution.getEndTime(),
            stepExecution.getStatus().toString(),
            stepExecution.getCommitCount(),
            stepExecution.getReadCount(),
            stepExecution.getFilterCount(),
            stepExecution.getWriteCount(),
            stepExecution.getExitStatus().getExitCode(),
            exitDescription,
            version,
            stepExecution.getReadSkipCount(),
            stepExecution.getProcessSkipCount(),
            stepExecution.getWriteSkipCount(),
            stepExecution.getRollbackCount(),
            stepExecution.getLastUpdated(),
            stepExecution.getId(),
            stepExecution.getVersion()
          };
      int count =
          getJdbcTemplate()
              .update(
                  getQuery(UPDATE_STEP_EXECUTION),
                  parameters,
                  new int[] {
                    Types.TIMESTAMP,
                    Types.TIMESTAMP,
                    Types.VARCHAR,
                    Types.INTEGER,
                    Types.INTEGER,
                    Types.INTEGER,
                    Types.INTEGER,
                    Types.VARCHAR,
                    Types.VARCHAR,
                    Types.INTEGER,
                    Types.INTEGER,
                    Types.INTEGER,
                    Types.INTEGER,
                    Types.INTEGER,
                    Types.TIMESTAMP,
                    Types.BIGINT,
                    Types.INTEGER
                  });

      // Avoid concurrent modifications...
      if (count == 0) {
        int curentVersion =
            getJdbcTemplate()
                .queryForObject(
                    getQuery(CURRENT_VERSION_STEP_EXECUTION),
                    new Object[] {stepExecution.getId()},
                    Integer.class);
        throw new OptimisticLockingFailureException(
            "Attempt to update step execution id="
                + stepExecution.getId()
                + " with wrong version ("
                + stepExecution.getVersion()
                + "), where current version is "
                + curentVersion);
      }

      stepExecution.incrementVersion();
    }
  }