Package org.springframework.batch.core.step.item

Source Code of org.springframework.batch.core.step.item.TaskletStepExceptionTests$FailingRollbackTransactionManager

/*
* Copyright 2008-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.core.step.item;

import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.core.step.tasklet.TaskletStep;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemStreamSupport;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Collection;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.springframework.batch.core.BatchStatus.COMPLETED;
import static org.springframework.batch.core.BatchStatus.FAILED;
import static org.springframework.batch.core.BatchStatus.STOPPED;
import static org.springframework.batch.core.BatchStatus.UNKNOWN;

/**
* Tests for the behavior of TaskletStep in a failure scenario.
*
* @author Lucas Ward
* @author Dave Syer
* @author David Turanski
*
*/
public class TaskletStepExceptionTests {

  TaskletStep taskletStep;

  StepExecution stepExecution;

  UpdateCountingJobRepository jobRepository;

  static RuntimeException taskletException = new RuntimeException("Static planned test exception.");

  static JobInterruptedException interruptedException = new JobInterruptedException("");

  @Before
  public void init() {
    taskletStep = new TaskletStep();
    taskletStep.setTasklet(new ExceptionTasklet());
    jobRepository = new UpdateCountingJobRepository();
    taskletStep.setJobRepository(jobRepository);
    taskletStep.setTransactionManager(new ResourcelessTransactionManager());

    JobInstance jobInstance = new JobInstance(1L, "testJob");
    JobExecution jobExecution = new JobExecution(jobInstance, new JobParameters());
    stepExecution = new StepExecution("testStep", jobExecution);
  }

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

  @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 testInterruptedWithCustomStatus() throws Exception {
    taskletStep.setTasklet(new Tasklet() {
      @Override
      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 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 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() {

      @Override
      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());
  }

  @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 testCloseError() throws Exception {

    final RuntimeException exception = new RuntimeException();
    taskletStep.setStreams(new ItemStream[] { new ItemStreamSupport() {
      @Override
      public void close() throws ItemStreamException {
        super.close();
        throw exception;
      }
    } });

    taskletStep.execute(stepExecution);
    assertEquals(FAILED, stepExecution.getStatus());
    assertTrue(stepExecution.getFailureExceptions().contains(taskletException));
    assertTrue(stepExecution.getFailureExceptions().contains(exception));
    assertEquals(2, jobRepository.getUpdateCount());
  }

  @SuppressWarnings("serial")
  @Test
  public void testCommitError() throws Exception {

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

      @Override
      protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
        throw new RuntimeException("foo");
      }
    });

    taskletStep.setTasklet(new Tasklet() {

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

    });

    taskletStep.execute(stepExecution);
    assertEquals(UNKNOWN, stepExecution.getStatus());
    Throwable e = stepExecution.getFailureExceptions().get(0);
    assertEquals("foo", e.getMessage());
    assertEquals(0, stepExecution.getCommitCount());
    assertEquals(1, stepExecution.getRollbackCount()); // Failed transaction
    // counts as
    // rollback
    assertEquals(2, stepExecution.getExecutionContext().size());
    assertTrue(stepExecution.getExecutionContext().containsKey(Step.STEP_TYPE_KEY));
    assertTrue(stepExecution.getExecutionContext().containsKey(TaskletStep.TASKLET_TYPE_KEY));
  }

  @SuppressWarnings("serial")
  @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() {

      @Override
      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(2, stepExecution.getExecutionContext().size());
    assertTrue(stepExecution.getExecutionContext().containsKey(Step.STEP_TYPE_KEY));
    assertTrue(stepExecution.getExecutionContext().containsKey(TaskletStep.TASKLET_TYPE_KEY));
  }

  @Test
  public void testRepositoryErrorOnExecutionContext() throws Exception {

    taskletStep.setTasklet(new Tasklet() {

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

    });

    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 testRepositoryErrorOnExecutionContextInTransaction() throws Exception {

    taskletStep.setTasklet(new Tasklet() {

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

    });

    jobRepository.setFailOnUpdateExecutionContext(true);
    jobRepository.setFailInTransaction(true);
    taskletStep.execute(stepExecution);
    assertEquals(FAILED, stepExecution.getStatus());
    Throwable e = stepExecution.getFailureExceptions().get(0);
    assertEquals("JobRepository failure forcing rollback", e.getMessage());

  }

  @Test
  public void testRepositoryErrorOnExecutionContextInTransactionRollbackFailed() throws Exception {

    taskletStep.setTasklet(new Tasklet() {

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

    });
   
    taskletStep.setTransactionManager(new FailingRollbackTransactionManager());

    jobRepository.setFailOnUpdateExecutionContext(true);
    jobRepository.setFailInTransaction(true);
    taskletStep.execute(stepExecution);
    assertEquals(UNKNOWN, stepExecution.getStatus());
    Throwable e = stepExecution.getFailureExceptions().get(0);
    assertEquals("Expected exception in rollback", e.getMessage());

  }

  @Test
  public void testRepositoryErrorOnUpdateStepExecution() throws Exception {

    taskletStep.setTasklet(new Tasklet() {

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

    });

    jobRepository.setFailOnUpdateStepExecution(2);
    taskletStep.execute(stepExecution);
    assertEquals(UNKNOWN, stepExecution.getStatus());
    Throwable e = stepExecution.getFailureExceptions().get(0);
    assertEquals("Expected exception in step execution persistence", e.getMessage());

  }

  @Test
  public void testRepositoryErrorOnUpdateStepExecutionInTransaction() throws Exception {

    taskletStep.setTasklet(new Tasklet() {

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

    });

    jobRepository.setFailOnUpdateStepExecution(1);
    jobRepository.setFailInTransaction(true);
    taskletStep.execute(stepExecution);
    assertEquals(FAILED, stepExecution.getStatus());
    Throwable e = stepExecution.getFailureExceptions().get(0);
    assertEquals("JobRepository failure forcing rollback", e.getMessage());

  }

  @Test
  public void testRepositoryErrorOnUpdateStepExecutionInTransactionRollbackFailed() throws Exception {

    taskletStep.setTasklet(new Tasklet() {

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

    });
   
    taskletStep.setTransactionManager(new FailingRollbackTransactionManager());

    jobRepository.setFailOnUpdateStepExecution(1);
    jobRepository.setFailInTransaction(true);
    taskletStep.execute(stepExecution);
    assertEquals(UNKNOWN, stepExecution.getStatus());
    Throwable e = stepExecution.getFailureExceptions().get(0);
    assertEquals("Expected exception in rollback", e.getMessage());

  }

  @Test
  public void testRepositoryErrorOnFailure() throws Exception {

    taskletStep.setTasklet(new Tasklet() {

      @Override
      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 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));
  }

  private static class ExceptionTasklet implements Tasklet {

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
      throw taskletException;
    }
  }

  private static class InterruptionListener extends StepExecutionListenerSupport {

    @Override
    public void beforeStep(StepExecution stepExecution) {
      stepExecution.setTerminateOnly();
    }
  }

  private static class UpdateCountingJobRepository implements JobRepository {

    private int updateCount = 0;

    private boolean failOnUpdateContext = false;

    private int failOnUpdateExecution = -1;

    private boolean failInTransaction = false;

    public void setFailOnUpdateExecutionContext(boolean failOnUpdate) {
      this.failOnUpdateContext = failOnUpdate;
    }

    public void setFailOnUpdateStepExecution(int failOnUpdate) {
      this.failOnUpdateExecution = failOnUpdate;
    }

    public void setFailInTransaction(boolean failInTransaction) {
      this.failInTransaction = failInTransaction;
    }

    @Override
    public void add(StepExecution stepExecution) {
    }

    @Override
    public JobExecution createJobExecution(String jobName, JobParameters jobParameters)
        throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
      return null;
    }

    @Override
    public StepExecution getLastStepExecution(JobInstance jobInstance, String stepName) {
      return null;
    }

    @Override
    public int getStepExecutionCount(JobInstance jobInstance, String stepName) {
      return 0;
    }

    @Override
    public boolean isJobInstanceExists(String jobName, JobParameters jobParameters) {
      return false;
    }

    @Override
    public void update(JobExecution jobExecution) {
    }

    @Override
    public void update(StepExecution stepExecution) {
      if (updateCount++ == failOnUpdateExecution) {
        if (!failInTransaction
            || (failInTransaction && TransactionSynchronizationManager.isActualTransactionActive())) {
          throw new RuntimeException("Expected exception in step execution persistence");
        }
      }
    }

    @Override
    public void updateExecutionContext(StepExecution stepExecution) {
      if (failOnUpdateContext) {
        if (!failInTransaction
            || (failInTransaction && TransactionSynchronizationManager.isActualTransactionActive())) {
          throw new RuntimeException("Expected exception in step execution context persistence");
        }
      }
    }

    public int getUpdateCount() {
      return updateCount;
    }

    @Override
    public JobExecution getLastJobExecution(String jobName, JobParameters jobParameters) {
      return null;
    }

    @Override
    public void updateExecutionContext(JobExecution jobExecution) {
    }

    @Override
    public void addAll(Collection<StepExecution> stepExecutions) {
    }

    @Override
    public JobInstance createJobInstance(String jobName,
        JobParameters jobParameters) {
      return null;
    }

    @Override
    public JobExecution createJobExecution(JobInstance jobInstance,
        JobParameters jobParameters, String jobConfigurationLocation) {
      return null;
    }
  }
 
  @SuppressWarnings("serial")
  private static class FailingRollbackTransactionManager extends ResourcelessTransactionManager {
   
    @Override
    protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
      super.doRollback(status);
      throw new RuntimeException("Expected exception in rollback");
    }   
  }

}
TOP

Related Classes of org.springframework.batch.core.step.item.TaskletStepExceptionTests$FailingRollbackTransactionManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.