Package org.springframework.batch.core.job

Source Code of org.springframework.batch.core.job.SimpleJobTests

/*
* Copyright 2006-2013 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.job;

import static org.mockito.Mockito.mock;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.listener.JobExecutionListenerSupport;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.dao.ExecutionContextDao;
import org.springframework.batch.core.repository.dao.JobExecutionDao;
import org.springframework.batch.core.repository.dao.JobInstanceDao;
import org.springframework.batch.core.repository.dao.MapExecutionContextDao;
import org.springframework.batch.core.repository.dao.MapJobExecutionDao;
import org.springframework.batch.core.repository.dao.MapJobInstanceDao;
import org.springframework.batch.core.repository.dao.MapStepExecutionDao;
import org.springframework.batch.core.repository.dao.StepExecutionDao;
import org.springframework.batch.core.repository.support.SimpleJobRepository;
import org.springframework.batch.core.step.StepSupport;
import org.springframework.batch.item.ExecutionContext;

/**
* Tests for DefaultJobLifecycle. MapJobDao and MapStepExecutionDao are used
* instead of a mock repository to test that status is being stored correctly.
*
* @author Lucas Ward
* @author Will Schipp
*/
public class SimpleJobTests {

  private JobRepository jobRepository;

  private JobInstanceDao jobInstanceDao;

  private JobExecutionDao jobExecutionDao;

  private StepExecutionDao stepExecutionDao;

  private ExecutionContextDao ecDao;

  private List<Serializable> list = new ArrayList<Serializable>();

  private JobInstance jobInstance;

  private JobExecution jobExecution;

  private StepExecution stepExecution1;

  private StepExecution stepExecution2;

  private StubStep step1;

  private StubStep step2;

  private JobParameters jobParameters = new JobParameters();

  private SimpleJob job;

  @Before
  public void setUp() throws Exception {

    jobInstanceDao = new MapJobInstanceDao();
    jobExecutionDao = new MapJobExecutionDao();
    stepExecutionDao = new MapStepExecutionDao();
    ecDao = new MapExecutionContextDao();
    jobRepository = new SimpleJobRepository(jobInstanceDao, jobExecutionDao, stepExecutionDao, ecDao);
    job = new SimpleJob();
    job.setJobRepository(jobRepository);

    step1 = new StubStep("TestStep1", jobRepository);
    step1.setCallback(new Runnable() {
      @Override
      public void run() {
        list.add("default");
      }
    });
    step2 = new StubStep("TestStep2", jobRepository);
    step2.setCallback(new Runnable() {
      @Override
      public void run() {
        list.add("default");
      }
    });

    List<Step> steps = new ArrayList<Step>();
    steps.add(step1);
    steps.add(step2);
    job.setName("testJob");
    job.setSteps(steps);

    jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
    jobInstance = jobExecution.getJobInstance();

    stepExecution1 = new StepExecution(step1.getName(), jobExecution);
    stepExecution2 = new StepExecution(step2.getName(), jobExecution);

  }

  /**
   * Test method for {@link SimpleJob#setSteps(java.util.List)}.
   */
  @Test
  public void testSetSteps() {
    job.setSteps(Collections.singletonList((Step) new StepSupport("step")));
    job.execute(jobExecution);
    assertEquals(1, jobExecution.getStepExecutions().size());
  }

  /**
   * Test method for {@link SimpleJob#setSteps(java.util.List)}.
   */
  @Test
  public void testGetSteps() {
    assertEquals(2, job.getStepNames().size());
  }

  /**
   * Test method for
   * {@link SimpleJob#addStep(org.springframework.batch.core.Step)}.
   */
  @Test
  public void testAddStep() {
    job.setSteps(Collections.<Step> emptyList());
    job.addStep(new StepSupport("step"));
    job.execute(jobExecution);
    assertEquals(1, jobExecution.getStepExecutions().size());
  }

  // Test to ensure the exit status returned by the last step is returned
  @Test
  public void testExitStatusReturned() throws JobExecutionException {

    final ExitStatus customStatus = new ExitStatus("test");

    Step testStep = new Step() {

      @Override
      public void execute(StepExecution stepExecution) throws JobInterruptedException {
        stepExecution.setExitStatus(customStatus);
      }

      @Override
      public String getName() {
        return "test";
      }

      @Override
      public int getStartLimit() {
        return 1;
      }

      @Override
      public boolean isAllowStartIfComplete() {
        return false;
      }
    };
    List<Step> steps = new ArrayList<Step>();
    steps.add(testStep);
    job.setSteps(steps);
    job.execute(jobExecution);
    assertEquals(customStatus, jobExecution.getExitStatus());
  }

  @Test
  public void testRunNormally() throws Exception {
    step1.setStartLimit(5);
    step2.setStartLimit(5);
    job.execute(jobExecution);
    assertEquals(2, list.size());
    checkRepository(BatchStatus.COMPLETED);
    assertNotNull(jobExecution.getEndTime());
    assertNotNull(jobExecution.getStartTime());

    assertTrue(step1.passedInJobContext.isEmpty());
    assertFalse(step2.passedInJobContext.isEmpty());
  }

  @Test
  public void testRunNormallyWithListener() throws Exception {
    job.setJobExecutionListeners(new JobExecutionListenerSupport[] { new JobExecutionListenerSupport() {
      @Override
      public void beforeJob(JobExecution jobExecution) {
        list.add("before");
      }

      @Override
      public void afterJob(JobExecution jobExecution) {
        list.add("after");
      }
    } });
    job.execute(jobExecution);
    assertEquals(4, list.size());
  }

  @Test
  public void testRunWithSimpleStepExecutor() throws Exception {

    job.setJobRepository(jobRepository);
    // do not set StepExecutorFactory...
    step1.setStartLimit(5);
    step2.setStartLimit(5);
    job.execute(jobExecution);
    assertEquals(2, list.size());
    checkRepository(BatchStatus.COMPLETED, ExitStatus.COMPLETED);

  }

  @Test
  public void testExecutionContextIsSet() throws Exception {
    testRunNormally();
    assertEquals(jobInstance, jobExecution.getJobInstance());
    assertEquals(2, jobExecution.getStepExecutions().size());
    assertEquals(step1.getName(), stepExecution1.getStepName());
    assertEquals(step2.getName(), stepExecution2.getStepName());
  }

  @Test
  public void testInterrupted() throws Exception {
    step1.setStartLimit(5);
    step2.setStartLimit(5);
    final JobInterruptedException exception = new JobInterruptedException("Interrupt!");
    step1.setProcessException(exception);
    job.execute(jobExecution);
    assertEquals(1, jobExecution.getAllFailureExceptions().size());
    assertEquals(exception, jobExecution.getStepExecutions().iterator().next().getFailureExceptions().get(0));
    assertEquals(0, list.size());
    checkRepository(BatchStatus.STOPPED, ExitStatus.STOPPED);
  }

  @Test
  public void testInterruptedAfterUnknownStatus() throws Exception {
    step1.setStartLimit(5);
    step2.setStartLimit(5);
    final JobInterruptedException exception = new JobInterruptedException("Interrupt!", BatchStatus.UNKNOWN);
    step1.setProcessException(exception);
    job.execute(jobExecution);
    assertEquals(1, jobExecution.getAllFailureExceptions().size());
    assertEquals(exception, jobExecution.getStepExecutions().iterator().next().getFailureExceptions().get(0));
    assertEquals(0, list.size());
    checkRepository(BatchStatus.UNKNOWN, ExitStatus.STOPPED);
  }

  @Test
  public void testFailed() throws Exception {
    step1.setStartLimit(5);
    step2.setStartLimit(5);
    final RuntimeException exception = new RuntimeException("Foo!");
    step1.setProcessException(exception);

    job.execute(jobExecution);
    assertEquals(1, jobExecution.getAllFailureExceptions().size());
    assertEquals(exception, jobExecution.getAllFailureExceptions().get(0));
    assertEquals(0, list.size());
    assertEquals(BatchStatus.FAILED, jobExecution.getStatus());
    checkRepository(BatchStatus.FAILED, ExitStatus.FAILED);
  }

  @Test
  public void testFailedWithListener() throws Exception {
    job.setJobExecutionListeners(new JobExecutionListenerSupport[] { new JobExecutionListenerSupport() {
      @Override
      public void afterJob(JobExecution jobExecution) {
        list.add("afterJob");
      }
    } });
    final RuntimeException exception = new RuntimeException("Foo!");
    step1.setProcessException(exception);

    job.execute(jobExecution);
    assertEquals(1, jobExecution.getAllFailureExceptions().size());
    assertEquals(exception, jobExecution.getAllFailureExceptions().get(0));
    assertEquals(1, list.size());
    checkRepository(BatchStatus.FAILED, ExitStatus.FAILED);
  }

  @Test
  public void testFailedWithError() throws Exception {
    step1.setStartLimit(5);
    step2.setStartLimit(5);
    final Error exception = new Error("Foo!");
    step1.setProcessException(exception);

    job.execute(jobExecution);
    assertEquals(1, jobExecution.getAllFailureExceptions().size());
    assertEquals(exception, jobExecution.getAllFailureExceptions().get(0));
    assertEquals(0, list.size());
    checkRepository(BatchStatus.FAILED, ExitStatus.FAILED);
  }

  @Test
  public void testStepShouldNotStart() throws Exception {
    // Start policy will return false, keeping the step from being started.
    step1.setStartLimit(0);

    job.execute(jobExecution);

    assertEquals(1, jobExecution.getFailureExceptions().size());
    Throwable ex = jobExecution.getFailureExceptions().get(0);
    assertTrue("Wrong message in exception: " + ex.getMessage(),
        ex.getMessage().indexOf("start limit exceeded") >= 0);
  }

  @Test
  public void testStepAlreadyComplete() throws Exception {
    stepExecution1.setStatus(BatchStatus.COMPLETED);
    jobRepository.add(stepExecution1);
    jobExecution.setEndTime(new Date());
    jobRepository.update(jobExecution);
    jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
    job.execute(jobExecution);
    assertEquals(0, jobExecution.getFailureExceptions().size());
    assertEquals(1, jobExecution.getStepExecutions().size());
    assertEquals(stepExecution2.getStepName(), jobExecution.getStepExecutions().iterator().next().getStepName());
  }

  @Test
  public void testStepAlreadyCompleteInSameExecution() throws Exception {
    List<Step> steps = new ArrayList<Step>();
    steps.add(step1);
    steps.add(step2);
    // Two steps with the same name should both be executed, since
    // the user might actually want it to happen twice.  On a restart
    // it would be executed twice again, even if it failed on the
    // second execution.  This seems reasonable.
    steps.add(step2);
    job.setSteps(steps);
    job.execute(jobExecution);
    assertEquals(0, jobExecution.getFailureExceptions().size());
    assertEquals(3, jobExecution.getStepExecutions().size());
    assertEquals(stepExecution1.getStepName(), jobExecution.getStepExecutions().iterator().next().getStepName());
  }

  @Test
  public void testNoSteps() throws Exception {
    job.setSteps(new ArrayList<Step>());

    job.execute(jobExecution);
    ExitStatus exitStatus = jobExecution.getExitStatus();
    assertTrue("Wrong message in execution: " + exitStatus, exitStatus.getExitDescription().indexOf(
        "no steps configured") >= 0);
  }

  @Test
  public void testNotExecutedIfAlreadyStopped() throws Exception {
    jobExecution.stop();
    job.execute(jobExecution);

    assertEquals(0, list.size());
    checkRepository(BatchStatus.STOPPED, ExitStatus.NOOP);
    ExitStatus exitStatus = jobExecution.getExitStatus();
    assertEquals(ExitStatus.NOOP.getExitCode(), exitStatus.getExitCode());
  }

  @Test
  public void testRestart() throws Exception {
    step1.setAllowStartIfComplete(true);
    final RuntimeException exception = new RuntimeException("Foo!");
    step2.setProcessException(exception);

    job.execute(jobExecution);
    Throwable e = jobExecution.getAllFailureExceptions().get(0);
    assertSame(exception, e);

    jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
    job.execute(jobExecution);
    e = jobExecution.getAllFailureExceptions().get(0);
    assertSame(exception, e);
    assertTrue(step1.passedInStepContext.isEmpty());
    assertFalse(step2.passedInStepContext.isEmpty());
  }

  @Test
  public void testRestartWithNullParameter() throws Exception {

    JobParameters jobParameters = new JobParametersBuilder().addString("foo", null).toJobParameters();
    jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
    jobInstance = jobExecution.getJobInstance();

    step1.setAllowStartIfComplete(true);
    final RuntimeException exception = new RuntimeException("Foo!");
    step2.setProcessException(exception);

    job.execute(jobExecution);
    Throwable e = jobExecution.getAllFailureExceptions().get(0);
    assertSame(exception, e);

    jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
    job.execute(jobExecution);
    e = jobExecution.getAllFailureExceptions().get(0);
    assertSame(exception, e);
    assertTrue(step1.passedInStepContext.isEmpty());
    assertFalse(step2.passedInStepContext.isEmpty());
  }

  @Test
  public void testInterruptWithListener() throws Exception {
    step1.setProcessException(new JobInterruptedException("job interrupted!"));

    JobExecutionListener listener = mock(JobExecutionListener.class);
    listener.beforeJob(jobExecution);
    listener.afterJob(jobExecution);

    job.setJobExecutionListeners(new JobExecutionListener[] { listener });

    job.execute(jobExecution);
    assertEquals(BatchStatus.STOPPED, jobExecution.getStatus());

  }

  /**
   * Execution context should be restored on restart.
   */
  @Test
  public void testRestartAndExecutionContextRestored() throws Exception {

    job.setRestartable(true);

    step1.setAllowStartIfComplete(true);
    final RuntimeException exception = new RuntimeException("Foo!");
    step2.setProcessException(exception);

    job.execute(jobExecution);
    assertEquals(1, jobExecution.getAllFailureExceptions().size());
    Throwable e = jobExecution.getAllFailureExceptions().get(0);
    assertSame(exception, e);

    assertTrue(step1.passedInJobContext.isEmpty());
    assertFalse(step2.passedInJobContext.isEmpty());

    assertFalse(jobExecution.getExecutionContext().isEmpty());

    jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);

    job.execute(jobExecution);
    assertEquals(1, jobExecution.getAllFailureExceptions().size());
    e = jobExecution.getAllFailureExceptions().get(0);
    assertSame(exception, e);
    assertFalse(step1.passedInJobContext.isEmpty());
    assertFalse(step2.passedInJobContext.isEmpty());
  }

  @Test
  public void testInterruptJob() throws Exception {

    step1 = new StubStep("interruptStep", jobRepository) {

      @Override
      public void execute(StepExecution stepExecution) throws JobInterruptedException,
      UnexpectedJobExecutionException {
        stepExecution.getJobExecution().stop();
        super.execute(stepExecution);
      }

    };

    job.setSteps(Arrays.asList(new Step[] { step1, step2 }));
    job.execute(jobExecution);
    assertEquals(BatchStatus.STOPPED, jobExecution.getStatus());
    assertEquals(1, jobExecution.getAllFailureExceptions().size());
    Throwable expected = jobExecution.getAllFailureExceptions().get(0);
    assertTrue("Wrong exception " + expected, expected instanceof JobInterruptedException);
    assertEquals("JobExecution interrupted.", expected.getMessage());

    assertNull("Second step was not supposed to be executed", step2.passedInStepContext);
  }

  @Test
  public void testGetStepExists() {
    step1 = new StubStep("step1", jobRepository);
    step2 = new StubStep("step2", jobRepository);
    job.setSteps(Arrays.asList(new Step[] { step1, step2 }));
    Step step = job.getStep("step2");
    assertNotNull(step);
    assertEquals("step2", step.getName());
  }

  @Test
  public void testGetStepNotExists() {
    step1 = new StubStep("step1", jobRepository);
    step2 = new StubStep("step2", jobRepository);
    job.setSteps(Arrays.asList(new Step[] { step1, step2 }));
    Step step = job.getStep("foo");
    assertNull(step);
  }

  /*
   * Check JobRepository to ensure status is being saved.
   */
  private void checkRepository(BatchStatus status, ExitStatus exitStatus) {
    assertEquals(jobInstance, jobInstanceDao.getJobInstance(job.getName(), jobParameters));
    // because map DAO stores in memory, it can be checked directly
    JobExecution jobExecution = jobExecutionDao.findJobExecutions(jobInstance).get(0);
    assertEquals(jobInstance.getId(), jobExecution.getJobId());
    assertEquals(status, jobExecution.getStatus());
    if (exitStatus != null) {
      assertEquals(exitStatus.getExitCode(), jobExecution.getExitStatus().getExitCode());
    }
  }

  private void checkRepository(BatchStatus status) {
    checkRepository(status, null);
  }

  private static class StubStep extends StepSupport {

    private Runnable runnable;

    private Throwable exception;

    private JobRepository jobRepository;

    private ExecutionContext passedInStepContext;

    private ExecutionContext passedInJobContext;

    /**
     * @param string
     */
    public StubStep(String string, JobRepository jobRepository) {
      super(string);
      this.jobRepository = jobRepository;
    }

    /**
     * @param exception
     */
    public void setProcessException(Throwable exception) {
      this.exception = exception;
    }

    /**
     * @param runnable
     */
    public void setCallback(Runnable runnable) {
      this.runnable = runnable;
    }

    /*
     * (non-Javadoc)
     *
     * @seeorg.springframework.batch.core.step.StepSupport#execute(org.
     * springframework.batch.core.StepExecution)
     */
    @Override
    public void execute(StepExecution stepExecution) throws JobInterruptedException,
    UnexpectedJobExecutionException {

      passedInJobContext = new ExecutionContext(stepExecution.getJobExecution().getExecutionContext());
      passedInStepContext = new ExecutionContext(stepExecution.getExecutionContext());
      stepExecution.getExecutionContext().putString("stepKey", "stepValue");
      stepExecution.getJobExecution().getExecutionContext().putString("jobKey", "jobValue");
      jobRepository.update(stepExecution);
      jobRepository.updateExecutionContext(stepExecution);

      if (exception instanceof JobInterruptedException) {
        stepExecution.setExitStatus(ExitStatus.FAILED);
        stepExecution.setStatus(((JobInterruptedException) exception).getStatus());
        stepExecution.addFailureException(exception);
        throw (JobInterruptedException) exception;
      }
      if (exception instanceof RuntimeException) {
        stepExecution.setExitStatus(ExitStatus.FAILED);
        stepExecution.setStatus(BatchStatus.FAILED);
        stepExecution.addFailureException(exception);
        return;
      }
      if (exception instanceof Error) {
        stepExecution.setExitStatus(ExitStatus.FAILED);
        stepExecution.setStatus(BatchStatus.FAILED);
        stepExecution.addFailureException(exception);
        return;
      }
      if (exception instanceof JobInterruptedException) {
        stepExecution.setExitStatus(ExitStatus.FAILED);
        stepExecution.setStatus(BatchStatus.FAILED);
        stepExecution.addFailureException(exception);
        return;
      }
      if (runnable != null) {
        runnable.run();
      }
      stepExecution.setExitStatus(ExitStatus.COMPLETED);
      stepExecution.setStatus(BatchStatus.COMPLETED);
      jobRepository.update(stepExecution);
      jobRepository.updateExecutionContext(stepExecution);

    }

  }
}
TOP

Related Classes of org.springframework.batch.core.job.SimpleJobTests

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.