@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());
  }
  public void afterJob(JobExecution jobExecution) {
    StringBuilder protocol = new StringBuilder();
    protocol.append("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++ \n");
    protocol.append("Protocol for " + jobExecution.getJobInstance().getJobName() + " \n");
    protocol.append("  Started     : " + jobExecution.getStartTime() + "\n");
    protocol.append("  Finished    : " + jobExecution.getEndTime() + "\n");
    protocol.append("  Exit-Code   : " + jobExecution.getExitStatus().getExitCode() + "\n");
    protocol.append("  Exit-Descr. : " + jobExecution.getExitStatus().getExitDescription() + "\n");
    protocol.append("  Status      : " + jobExecution.getStatus() + "\n");
    protocol.append("+++++++++++++++++++++++++++++++++++++++++++++++++++++++ \n");

    protocol.append("Job-Parameter: \n");
    JobParameters jp = jobExecution.getJobParameters();
    for (Iterator<Entry<String, JobParameter>> iter = jp.getParameters().entrySet().iterator();
        iter.hasNext(); ) {
      Entry<String, JobParameter> entry = iter.next();
      protocol.append("  " + entry.getKey() + "=" + entry.getValue() + "\n");
    }
    protocol.append("+++++++++++++++++++++++++++++++++++++++++++++++++++++++ \n");

    for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
      protocol.append("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++ \n");
      protocol.append("Step " + stepExecution.getStepName() + " \n");
      protocol.append("WriteCount: " + stepExecution.getWriteCount() + "\n");
      protocol.append("Commits: " + stepExecution.getCommitCount() + "\n");
      protocol.append("SkipCount: " + stepExecution.getSkipCount() + "\n");
      protocol.append("Rollbacks: " + stepExecution.getRollbackCount() + "\n");
      protocol.append("Filter: " + stepExecution.getFilterCount() + "\n");
      protocol.append("+++++++++++++++++++++++++++++++++++++++++++++++++++++++ \n");
    }
    LOGGER.info(protocol.toString());
  }
 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;
 }
  @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();
    }
  }