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