IN PROGRESS - issue BATCH-825: Modify JobLauncher contract to not throw exception on job failure.

http://jira.springframework.org/browse/BATCH-825

Job, Step, and JobLauncher will not throw an exception if the job fails during processing, but rather will return the execution with an appropriate status and list of exceptions encountered.
This commit is contained in:
lucasward
2008-09-16 22:15:46 +00:00
parent fbe0443175
commit bbc34c13e7
20 changed files with 644 additions and 411 deletions

View File

@@ -32,12 +32,13 @@ public interface Job {
/**
* Run the {@link JobExecution} and update the meta information like status
* and statistics as necessary.
* and statistics as necessary. This method should not throw any exceptions
* for failed execution. Clients should be careful to inspect the {@link JobExecution}
* status to determine success or failure.
*
* @param execution a {@link JobExecution}
* @throws JobExecutionException
*/
void execute(JobExecution execution) throws JobExecutionException;
void execute(JobExecution execution);
/**
* @return in incrementer to be used for creating new parameters

View File

@@ -16,9 +16,11 @@
package org.springframework.batch.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.ExitStatus;
@@ -48,6 +50,8 @@ public class JobExecution extends Entity {
private volatile ExitStatus exitStatus = ExitStatus.UNKNOWN;
private volatile ExecutionContext executionContext = new ExecutionContext();
private volatile List<Throwable> failureExceptions = new ArrayList<Throwable>();
/**
* Because a JobExecution isn't valid unless the job is set, this
@@ -234,12 +238,63 @@ public class JobExecution extends Entity {
stepExecutions.add(stepExecution);
}
/**
* Get the date representing the last time this JobExecution was updated in the
* JobRepository.
*
* @return Date representing the last time this JobExecution was updated.
*/
public Date getLastUpdated() {
return lastUpdated;
}
/**
* Set the last time this JobExecution was updated.
*
* @param lastUpdated
*/
public void setLastUpdated(Date lastUpdated) {
this.lastUpdated = lastUpdated;
}
public List<Throwable> getFailureExceptions() {
return failureExceptions;
}
/**
* Set the list of failure causing exceptions for this JobExecution. It
* should be noted that the exceptions should only be for the job execution
* not step executions.
*
* @param failureExceptions
*/
public void setFailureExceptions(List<Throwable> failureExceptions) {
this.failureExceptions = failureExceptions;
}
/**
* Add the provided throwable to the failure exception list.
*
* @param t
*/
public void addFailureException(Throwable t){
this.failureExceptions.add(t);
}
/**
* Return all failure causing exceptions for this JobExecution, including
* step executions.
*
* @return List<Throwable> containing all exceptions causing failure for this
* JobExecution.
*/
public List<Throwable> getAllFailureExceptions(){
List<Throwable> allExceptions = new ArrayList<Throwable>(failureExceptions);
for(StepExecution stepExecution: stepExecutions){
allExceptions.addAll(stepExecution.getFailureExceptions());
}
return allExceptions;
}
}

View File

@@ -16,7 +16,9 @@
package org.springframework.batch.core;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.ExitStatus;
@@ -68,6 +70,8 @@ public class StepExecution extends Entity {
private volatile boolean terminateOnly;
private volatile int filterCount;
private volatile List<Throwable> failureExceptions = new ArrayList<Throwable>();
/**
* Constructor with mandatory properties.
@@ -436,6 +440,18 @@ public class StepExecution extends Entity {
public void setLastUpdated(Date lastUpdated) {
this.lastUpdated = lastUpdated;
}
public List<Throwable> getFailureExceptions() {
return failureExceptions;
}
public void setFailureExceptions(List<Throwable> failureExceptions) {
this.failureExceptions = failureExceptions;
}
public void addFailureException(Throwable throwable){
this.failureExceptions.add(throwable);
}
/*
* (non-Javadoc)

View File

@@ -90,10 +90,9 @@ public class ApplicationContextJobFactory implements JobFactory {
/**
* @param execution
* @throws JobExecutionException
* @see org.springframework.batch.core.Job#execute(org.springframework.batch.core.JobExecution)
*/
public void execute(JobExecution execution) throws JobExecutionException {
public void execute(JobExecution execution) {
try {
delegate.execute(execution);
}

View File

@@ -28,7 +28,6 @@ import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.StartLimitExceededException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.ExitStatus;
@@ -50,7 +49,7 @@ public class SimpleJob extends AbstractJob {
* @throws StartLimitExceededException if start limit of one of the steps
* was exceeded
*/
public void execute(JobExecution execution) throws JobExecutionException {
public void execute(JobExecution execution){
JobInstance jobInstance = execution.getJobInstance();
@@ -97,6 +96,10 @@ public class SimpleJob extends AbstractJob {
step.execute(currentStepExecution);
if(currentStepExecution.getStatus() == BatchStatus.FAILED ||
currentStepExecution.getStatus() == BatchStatus.STOPPED ){
break;
}
}
}
@@ -105,20 +108,30 @@ public class SimpleJob extends AbstractJob {
throw new JobInterruptedException("JobExecution interrupted.");
}
updateStatus(execution, BatchStatus.COMPLETED);
updateStatus(execution, currentStepExecution.getStatus());
getCompositeListener().afterJob(execution);
//This is temporary, given the changes to how exceptions are handled, there really should only be an afterJob method
//but it's being left as is until the listener contract can be discussed.
if(execution.getStatus() == BatchStatus.COMPLETED){
getCompositeListener().afterJob(execution);
}
else if(execution.getStatus() == BatchStatus.FAILED){
getCompositeListener().onError(execution, currentStepExecution.getFailureExceptions().get(0));
}
else if(execution.getStatus() == BatchStatus.STOPPED){
getCompositeListener().onInterrupt(execution);
}
}
catch (JobInterruptedException e) {
execution.setStatus(BatchStatus.STOPPED);
execution.addFailureException(e);
getCompositeListener().onInterrupt(execution);
rethrow(e);
}
catch (Throwable t) {
execution.setStatus(BatchStatus.FAILED);
execution.addFailureException(t);
getCompositeListener().onError(execution, t);
rethrow(t);
}
finally {
ExitStatus status = ExitStatus.FAILED;
@@ -185,19 +198,4 @@ public class SimpleJob extends AbstractJob {
+ "StartMax: " + step.getStartLimit());
}
}
/**
* @param t
*/
private static void rethrow(Throwable t) throws RuntimeException {
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
else if (t instanceof Error) {
throw (Error) t;
}
else {
throw new UnexpectedJobExecutionException("Unexpected checked exception in job execution", t);
}
}
}

View File

@@ -37,10 +37,12 @@ import org.springframework.batch.core.repository.JobExecutionAlreadyRunningExcep
public interface JobLauncher {
/**
* Start a job execution for the given {@link Job} and {@link JobParameters}.
* Start a job execution for the given {@link Job} and {@link JobParameters}. If
* a JobExecution was able to be created successfully, it will always be returned
* by this method, regardless of whether or not the execution was successful.
*
* @return the exit code from the job if it returns synchronously. If the
* implementation is asynchronous, the status might well be unknown.
* @return the {@link JobExecution} if it returns synchronously. If the
* implementation is asynchronous, the status might well be unknown.
*
* @throws JobExecutionAlreadyRunningException if the JobInstance identified
* by the properties already has an execution running.

View File

@@ -99,8 +99,8 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean {
try {
logger.info("Job: [" + job + "] launched with the following parameters: [" + jobParameters + "]");
job.execute(jobExecution);
logger.info("Job: [" + job + "] completed successfully with the following parameters: ["
+ jobParameters + "]");
logger.info("Job: [" + job + "] completed with the following parameters: ["
+ jobParameters + "] and the following status: [" + jobExecution.getStatus() + "]");
}
catch (Throwable t) {
logger.info("Job: [" + job + "] failed with the following parameters: [" + jobParameters + "]", t);

View File

@@ -188,12 +188,8 @@ public abstract class AbstractStep implements Step, InitializingBean, BeanNameAw
try {
getCompositeListener().beforeStep(stepExecution);
try {
open(stepExecution.getExecutionContext());
}
catch (Exception e) {
throw new UnexpectedJobExecutionException("Failed to initialize the step", e);
}
open(stepExecution.getExecutionContext());
exitStatus = doExecute(stepExecution);
// Check if someone is trying to stop us
@@ -220,6 +216,7 @@ public abstract class AbstractStep implements Step, InitializingBean, BeanNameAw
logger.error("Encountered an error executing the step: " + e.getClass() + ": " + e.getMessage());
stepExecution.setStatus(determineBatchStatus(e));
exitStatus = getDefaultExitStatusForFailure(e);
stepExecution.addFailureException(e);
try {
exitStatus = exitStatus.and(getCompositeListener().onErrorInStep(stepExecution, e));
@@ -227,8 +224,8 @@ public abstract class AbstractStep implements Step, InitializingBean, BeanNameAw
}
catch (Exception ex) {
logger.error("Encountered an error on listener error callback.", ex);
stepExecution.addFailureException(ex);
}
rethrow(e);
}
finally {
@@ -252,35 +249,18 @@ public abstract class AbstractStep implements Step, InitializingBean, BeanNameAw
}
catch (Exception e) {
logger.error("Exception while closing step execution resources", e);
throw new UnexpectedJobExecutionException("Exception while closing step resources", e);
stepExecution.addFailureException(e);
}
if (commitException != null) {
stepExecution.setStatus(BatchStatus.UNKNOWN);
logger.error("Encountered an error saving batch meta data."
+ "This job is now in an unknown state and should not be restarted.", commitException);
throw new UnexpectedJobExecutionException("Encountered an error saving batch meta data.",
commitException);
stepExecution.addFailureException(commitException);
}
}
}
private static void rethrow(Throwable e) throws JobInterruptedException {
if (e instanceof Error) {
throw (Error) e;
}
if (e instanceof JobInterruptedException) {
throw (JobInterruptedException) e;
}
else if (e.getCause() instanceof JobInterruptedException) {
throw (JobInterruptedException) e.getCause();
}
else if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new UnexpectedJobExecutionException("Unexpected checked exception in step execution", e);
}
/**
* Determine the step status based on the exception.
*/

View File

@@ -15,21 +15,24 @@
*/
package org.springframework.batch.core;
import java.util.Date;
import static org.junit.Assert.*;
import junit.framework.TestCase;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang.SerializationUtils;
import org.junit.Test;
import org.springframework.batch.repeat.ExitStatus;
/**
* @author Dave Syer
*
*/
public class JobExecutionTests extends TestCase {
public class JobExecutionTests {
private JobExecution execution = new JobExecution(new JobInstance(new Long(11), new JobParameters(), "foo"), new Long(12));
@Test
public void testJobExecution() {
assertNull(new JobExecution(new JobInstance(null,null,"foo")).getId());
}
@@ -38,6 +41,7 @@ public class JobExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getEndTime()}.
*/
@Test
public void testGetEndTime() {
assertNull(execution.getEndTime());
execution.setEndTime(new Date(100L));
@@ -48,6 +52,7 @@ public class JobExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getEndTime()}.
*/
@Test
public void testIsRunning() {
assertTrue(execution.isRunning());
execution.setEndTime(new Date(100L));
@@ -58,6 +63,7 @@ public class JobExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getEndTime()}.
*/
@Test
public void testIsRunningWithStoppedExecution() {
assertTrue(execution.isRunning());
execution.stop();
@@ -69,6 +75,7 @@ public class JobExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getStartTime()}.
*/
@Test
public void testGetStartTime() {
execution.setStartTime(new Date(0L));
assertEquals(0L, execution.getStartTime().getTime());
@@ -78,6 +85,7 @@ public class JobExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getStatus()}.
*/
@Test
public void testGetStatus() {
assertEquals(BatchStatus.STARTING, execution.getStatus());
execution.setStatus(BatchStatus.COMPLETED);
@@ -88,6 +96,7 @@ public class JobExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getJobId()}.
*/
@Test
public void testGetJobId() {
assertEquals(11, execution.getJobId().longValue());
execution = new JobExecution(new JobInstance(new Long(23), new JobParameters(), "testJob"), null);
@@ -98,6 +107,7 @@ public class JobExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getJobId()}.
*/
@Test
public void testGetJobIdForNullJob() {
execution = new JobExecution(null, null);
assertEquals(null, execution.getJobId());
@@ -107,6 +117,7 @@ public class JobExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getJobId()}.
*/
@Test
public void testGetJob() {
assertNotNull(execution.getJobInstance());
}
@@ -115,22 +126,26 @@ public class JobExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getExitStatus()}.
*/
@Test
public void testGetExitCode() {
assertEquals(ExitStatus.UNKNOWN, execution.getExitStatus());
execution.setExitStatus(new ExitStatus(true, "23"));
assertEquals("23", execution.getExitStatus().getExitCode());
}
@Test
public void testContextContainsInfo() throws Exception {
assertEquals("foo", execution.getJobInstance().getJobName());
}
@Test
public void testAddAndRemoveStepExecution() throws Exception {
assertEquals(0, execution.getStepExecutions().size());
execution.createStepExecution("step");
assertEquals(1, execution.getStepExecutions().size());
}
@Test
public void testStop() throws Exception {
StepExecution stepExecution = execution.createStepExecution("step");
assertFalse(stepExecution.isTerminateOnly());
@@ -138,21 +153,42 @@ public class JobExecutionTests extends TestCase {
assertTrue(stepExecution.isTerminateOnly());
}
@Test
public void testToString() throws Exception {
assertTrue("JobExecution string does not contain id", execution.toString().indexOf("id=") >= 0);
assertTrue("JobExecution string does not contain name: " + execution, execution.toString().indexOf("foo") >= 0);
}
@Test
public void testToStringWithNullJob() throws Exception {
execution = new JobExecution(new JobInstance(null,null,"foo"));
assertTrue("JobExecution string does not contain id", execution.toString().indexOf("id=") >= 0);
assertTrue("JobExecution string does not contain job: " + execution, execution.toString().indexOf("job=") >= 0);
}
@Test
public void testSerialization() {
byte[] serialized = SerializationUtils.serialize(execution);
JobExecution deserialize = (JobExecution) SerializationUtils.deserialize(serialized);
assertEquals(execution, deserialize);
assertNotNull(deserialize.createStepExecution("foo"));
}
public void testFailureExceptions(){
RuntimeException exception = new RuntimeException();
assertEquals(0, execution.getFailureExceptions().size());
execution.addFailureException(exception);
assertEquals(1, execution.getFailureExceptions().size());
assertEquals(exception, execution.getFailureExceptions().get(0));
StepExecution stepExecution1 = execution.createStepExecution("execution1");
RuntimeException stepException1 = new RuntimeException();
stepExecution1.addFailureException(stepException1);
execution.createStepExecution("execution2");
List<Throwable> allExceptions = execution.getAllFailureExceptions();
assertEquals(2, allExceptions.size());
assertEquals(1, execution.getFailureExceptions().size());
assertTrue(allExceptions.contains(exception));
assertTrue(allExceptions.contains(stepException1));
}
}

View File

@@ -15,13 +15,15 @@
*/
package org.springframework.batch.core;
import static org.junit.Assert.*;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import junit.framework.TestCase;
import org.apache.commons.lang.SerializationUtils;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.step.StepSupport;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.ExitStatus;
@@ -30,7 +32,7 @@ import org.springframework.batch.repeat.ExitStatus;
* @author Dave Syer
*
*/
public class StepExecutionTests extends TestCase {
public class StepExecutionTests {
private StepExecution execution = newStepExecution(new StepSupport("stepName"), new Long(23));
@@ -40,15 +42,17 @@ public class StepExecutionTests extends TestCase {
@Override
protected void setUp() throws Exception {
@Before
public void setUp() throws Exception {
foobarEc.put("foo", "bar");
}
@Test
public void testStepExecution() {
assertNull(new StepExecution("step", null).getId());
}
@Test
public void testStepExecutionWithNullId() {
assertNull(new StepExecution("stepName", new JobExecution(new JobInstance(null,null,"foo"))).getId());
}
@@ -57,6 +61,7 @@ public class StepExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getEndTime()}.
*/
@Test
public void testGetEndTime() {
assertNull(execution.getEndTime());
execution.setEndTime(new Date(0L));
@@ -67,6 +72,7 @@ public class StepExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getStartTime()}.
*/
@Test
public void testGetStartTime() {
assertNotNull(execution.getStartTime());
execution.setStartTime(new Date(10L));
@@ -77,6 +83,7 @@ public class StepExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getStatus()}.
*/
@Test
public void testGetStatus() {
assertEquals(BatchStatus.STARTING, execution.getStatus());
execution.setStatus(BatchStatus.COMPLETED);
@@ -87,6 +94,7 @@ public class StepExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getJobId()}.
*/
@Test
public void testGetJobId() {
assertEquals(23, execution.getJobExecutionId().longValue());
}
@@ -95,6 +103,7 @@ public class StepExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.JobExecution#getExitStatus()}.
*/
@Test
public void testGetExitCode() {
assertEquals(ExitStatus.CONTINUABLE, execution.getExitStatus());
execution.setExitStatus(ExitStatus.FINISHED);
@@ -105,20 +114,24 @@ public class StepExecutionTests extends TestCase {
* Test method for
* {@link org.springframework.batch.core.StepExecution#getCommitCount()}.
*/
@Test
public void testGetCommitCount() {
execution.setCommitCount(123);
assertEquals(123, execution.getCommitCount());
}
@Test
public void testGetFilterCount() {
execution.setFilterCount(123);
assertEquals(123, execution.getFilterCount());
}
@Test
public void testGetJobExecution() throws Exception {
assertNotNull(execution.getJobExecution());
}
@Test
public void testApplyContribution() throws Exception {
StepContribution contribution = execution.createStepContribution();
contribution.incrementReadSkipCount();
@@ -134,12 +147,14 @@ public class StepExecutionTests extends TestCase {
assertEquals(1, execution.getFilterCount());
}
@Test
public void testTerminateOnly() throws Exception {
assertFalse(execution.isTerminateOnly());
execution.setTerminateOnly();
assertTrue(execution.isTerminateOnly());
}
@Test
public void testNullNameIsIllegal() throws Exception {
try {
new StepExecution(null, new JobExecution(new JobInstance(null, null, "job")));
@@ -150,6 +165,7 @@ public class StepExecutionTests extends TestCase {
}
}
@Test
public void testToString() throws Exception {
assertTrue("Should contain read count: " + execution.toString(), execution.toString().indexOf("read") >= 0);
assertTrue("Should contain write count: " + execution.toString(), execution.toString().indexOf("write") >= 0);
@@ -159,6 +175,7 @@ public class StepExecutionTests extends TestCase {
execution.toString().indexOf("rollback") >= 0);
}
@Test
public void testExecutionContext() throws Exception {
assertNotNull(execution.getExecutionContext());
ExecutionContext context = new ExecutionContext();
@@ -167,6 +184,7 @@ public class StepExecutionTests extends TestCase {
assertEquals("bar", execution.getExecutionContext().getString("foo"));
}
@Test
public void testEqualsWithSameIdentifier() throws Exception {
Step step = new StepSupport("stepName");
Entity stepExecution1 = newStepExecution(step, new Long(11));
@@ -174,30 +192,36 @@ public class StepExecutionTests extends TestCase {
assertEquals(stepExecution1, stepExecution2);
}
@Test
public void testEqualsWithNull() throws Exception {
Entity stepExecution = newStepExecution(new StepSupport("stepName"), new Long(11));
assertFalse(stepExecution.equals(null));
}
@Test
public void testEqualsWithNullIdentifiers() throws Exception {
Entity stepExecution = newStepExecution(new StepSupport("stepName"), new Long(11));
assertFalse(stepExecution.equals(blankExecution));
}
@Test
public void testEqualsWithNullJob() throws Exception {
Entity stepExecution = newStepExecution(new StepSupport("stepName"), new Long(11));
assertFalse(stepExecution.equals(blankExecution));
}
@Test
public void testEqualsWithSelf() throws Exception {
assertTrue(execution.equals(execution));
}
@Test
public void testEqualsWithDifferent() throws Exception {
Entity stepExecution = newStepExecution(new StepSupport("foo"), new Long(13));
assertFalse(execution.equals(stepExecution));
}
@Test
public void testEqualsWithNullStepId() throws Exception {
Step step = new StepSupport("name");
execution = newStepExecution(step, new Long(31));
@@ -207,15 +231,18 @@ public class StepExecutionTests extends TestCase {
assertTrue(execution.equals(stepExecution));
}
@Test
public void testHashCode() throws Exception {
assertTrue("Hash code same as parent", new Entity(execution.getId()).hashCode() != execution.hashCode());
}
@Test
public void testHashCodeWithNullIds() throws Exception {
assertTrue("Hash code not same as parent", new Entity(execution.getId()).hashCode() != blankExecution
.hashCode());
}
@Test
public void testHashCodeViaHashSet() throws Exception {
Set<StepExecution> set = new HashSet<StepExecution>();
set.add(execution);
@@ -224,6 +251,7 @@ public class StepExecutionTests extends TestCase {
assertTrue(set.contains(execution));
}
@Test
public void testSerialization() throws Exception {
ExitStatus status = ExitStatus.NOOP;
@@ -236,6 +264,16 @@ public class StepExecutionTests extends TestCase {
assertEquals(execution, deserialized);
assertEquals(status, deserialized.getExitStatus());
}
@Test
public void testAddException() throws Exception{
RuntimeException exception = new RuntimeException();
assertEquals(0, execution.getFailureExceptions().size());
execution.addFailureException(exception);
assertEquals(1, execution.getFailureExceptions().size());
assertEquals(exception, execution.getFailureExceptions().get(0));
}
private StepExecution newStepExecution(Step step, Long long2) {
JobInstance job = new JobInstance(new Long(3), new JobParameters(), "testJob");

View File

@@ -20,7 +20,6 @@ import java.util.Collections;
import junit.framework.TestCase;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.step.StepSupport;
@@ -31,7 +30,7 @@ import org.springframework.batch.core.step.StepSupport;
public class AbstractJobTests extends TestCase {
AbstractJob job = new AbstractJob("job") {
public void execute(JobExecution execution) throws JobExecutionException {
public void execute(JobExecution execution) {
throw new UnsupportedOperationException();
}
};
@@ -41,7 +40,7 @@ public class AbstractJobTests extends TestCase {
*/
public void testGetName() {
job = new AbstractJob(){
public void execute(JobExecution execution) throws JobExecutionException {
public void execute(JobExecution execution) {
// No-op
}
};
@@ -61,7 +60,7 @@ public class AbstractJobTests extends TestCase {
*/
public void testSetBeanNameWithNullName() {
job = new AbstractJob(null) {
public void execute(JobExecution execution) throws JobExecutionException {
public void execute(JobExecution execution) {
// NO-OP
}
};
@@ -103,7 +102,7 @@ public class AbstractJobTests extends TestCase {
public void testAfterPropertiesSet() throws Exception {
AbstractJob job = new AbstractJob() {
public void execute(JobExecution execution) throws JobExecutionException {
public void execute(JobExecution execution) {
}
};
job.setJobRepository(null);

View File

@@ -0,0 +1,19 @@
package org.springframework.batch.core.job;
import org.junit.Test;
/**
* Test suite for various failure scenarios during job processing.
*
* @author Lucas Ward
*
*/
public class SimpleJobFailureTests {
@Test
public void testStepFailure(){
}
}

View File

@@ -16,7 +16,9 @@
package org.springframework.batch.core.job;
import static org.easymock.EasyMock.*;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import java.io.Serializable;
import java.util.ArrayList;
@@ -32,7 +34,6 @@ 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.StartLimitExceededException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
@@ -217,13 +218,9 @@ public class SimpleJobTests extends TestCase {
step2.setStartLimit(5);
final JobInterruptedException exception = new JobInterruptedException("Interrupt!");
step1.setProcessException(exception);
try {
job.execute(jobExecution);
fail();
}
catch (UnexpectedJobExecutionException e) {
assertEquals(exception, e.getCause());
}
job.execute(jobExecution);
assertEquals(1, jobExecution.getAllFailureExceptions().size());
assertEquals(exception, jobExecution.getAllFailureExceptions().get(0));
assertEquals(0, list.size());
checkRepository(BatchStatus.STOPPED, ExitStatus.FAILED);
}
@@ -233,13 +230,10 @@ public class SimpleJobTests extends TestCase {
step2.setStartLimit(5);
final RuntimeException exception = new RuntimeException("Foo!");
step1.setProcessException(exception);
try {
job.execute(jobExecution);
fail();
}
catch (RuntimeException e) {
assertEquals(exception, e);
}
job.execute(jobExecution);
assertEquals(1, jobExecution.getAllFailureExceptions().size());
assertEquals(exception, jobExecution.getAllFailureExceptions().get(0));
assertEquals(0, list.size());
checkRepository(BatchStatus.FAILED, ExitStatus.FAILED);
}
@@ -253,15 +247,10 @@ public class SimpleJobTests extends TestCase {
final RuntimeException exception = new RuntimeException("Foo!");
step1.setProcessException(exception);
try {
job.execute(jobExecution);
fail();
}
catch (RuntimeException e) {
assertEquals(exception, e);
}
job.execute(jobExecution);
assertEquals(1, jobExecution.getAllFailureExceptions().size());
assertEquals(exception, jobExecution.getAllFailureExceptions().get(0));
assertEquals(1, list.size());
assertSame(exception, list.get(0));
checkRepository(BatchStatus.FAILED, ExitStatus.FAILED);
}
@@ -270,13 +259,10 @@ public class SimpleJobTests extends TestCase {
step2.setStartLimit(5);
final Error exception = new Error("Foo!");
step1.setProcessException(exception);
try {
job.execute(jobExecution);
fail();
}
catch (Error e) {
assertEquals(exception, e);
}
job.execute(jobExecution);
assertEquals(1, jobExecution.getAllFailureExceptions().size());
assertEquals(exception, jobExecution.getAllFailureExceptions().get(0));
assertEquals(0, list.size());
checkRepository(BatchStatus.FAILED, ExitStatus.FAILED);
}
@@ -285,15 +271,12 @@ public class SimpleJobTests extends TestCase {
// Start policy will return false, keeping the step from being started.
step1.setStartLimit(0);
try {
job.execute(jobExecution);
fail("Expected BatchCriticalException");
}
catch (StartLimitExceededException ex) {
// expected
assertTrue("Wrong message in exception: " + ex.getMessage(), ex.getMessage()
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);
}
}
public void testNoSteps() throws Exception {
@@ -320,13 +303,10 @@ public class SimpleJobTests extends TestCase {
public void testNotExecutedIfAlreadyStopped() throws Exception {
jobExecution.stop();
try {
job.execute(jobExecution);
fail();
}
catch (UnexpectedJobExecutionException e) {
assertTrue(e.getCause() instanceof JobInterruptedException);
}
job.execute(jobExecution);
assertEquals(1, jobExecution.getFailureExceptions().size());
assertTrue(jobExecution.getFailureExceptions().get(0) instanceof JobInterruptedException);
assertEquals(0, list.size());
checkRepository(BatchStatus.STOPPED, ExitStatus.NOOP);
ExitStatus exitStatus = jobExecution.getExitStatus();
@@ -338,24 +318,15 @@ public class SimpleJobTests extends TestCase {
final RuntimeException exception = new RuntimeException("Foo!");
step2.setProcessException(exception);
try {
job.execute(jobExecution);
fail();
}
catch (RuntimeException e) {
assertSame(exception, e);
}
job.execute(jobExecution);
Throwable e = jobExecution.getAllFailureExceptions().get(0);
assertSame(exception, e);
try {
job.execute(jobExecution);
fail();
}
catch (RuntimeException e) {
assertSame(exception, e);
}
job.execute(jobExecution);
e = jobExecution.getAllFailureExceptions().get(0);
assertSame(exception, e);
assertTrue(step1.passedInStepContext.isEmpty());
assertFalse(step2.passedInStepContext.isEmpty());
}
public void testInterruptWithListener() throws Exception {
@@ -368,13 +339,8 @@ public class SimpleJobTests extends TestCase {
job.setJobExecutionListeners(new JobExecutionListener[] { listener });
try {
job.execute(jobExecution);
fail();
}
catch (UnexpectedJobExecutionException e) {
// expected
}
job.execute(jobExecution);
assertEquals(BatchStatus.STOPPED, jobExecution.getStatus());
verify(listener);
}
@@ -390,13 +356,10 @@ public class SimpleJobTests extends TestCase {
final RuntimeException exception = new RuntimeException("Foo!");
step2.setProcessException(exception);
try {
job.execute(jobExecution);
fail();
}
catch (RuntimeException e) {
assertSame(exception, e);
}
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());
@@ -405,13 +368,10 @@ public class SimpleJobTests extends TestCase {
jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
try {
job.execute(jobExecution);
fail();
}
catch (RuntimeException e) {
assertSame(exception, e);
}
job.execute(jobExecution);
assertEquals(1, jobExecution.getAllFailureExceptions().size());
e = jobExecution.getAllFailureExceptions().get(0);
assertSame(exception, e);
assertFalse(step1.passedInJobContext.isEmpty());
assertFalse(step2.passedInJobContext.isEmpty());
}
@@ -428,16 +388,11 @@ public class SimpleJobTests extends TestCase {
};
job.setSteps(Arrays.asList(new Step[] { step1, step2 }));
try {
job.execute(jobExecution);
fail();
}
catch (UnexpectedJobExecutionException expected) {
assertTrue(expected.getCause() instanceof JobInterruptedException);
assertEquals("JobExecution interrupted.", expected.getCause().getMessage());
}
job.execute(jobExecution);
Throwable expected = jobExecution.getAllFailureExceptions().get(0);
assertTrue(expected instanceof JobInterruptedException);
assertEquals("JobExecution interrupted.", expected.getMessage());
assertNull("Second step was not executed", step2.passedInStepContext);
}
@@ -509,17 +464,20 @@ public class SimpleJobTests extends TestCase {
if (exception instanceof RuntimeException) {
stepExecution.setExitStatus(ExitStatus.FAILED);
stepExecution.setStatus(BatchStatus.FAILED);
throw (RuntimeException) exception;
stepExecution.addFailureException(exception);
return;
}
if (exception instanceof Error) {
stepExecution.setExitStatus(ExitStatus.FAILED);
stepExecution.setStatus(BatchStatus.FAILED);
throw (Error) exception;
stepExecution.addFailureException(exception);
return;
}
if (exception instanceof JobInterruptedException) {
stepExecution.setExitStatus(ExitStatus.FAILED);
stepExecution.setStatus(BatchStatus.FAILED);
throw (JobInterruptedException) exception;
stepExecution.setStatus(BatchStatus.STOPPED);
stepExecution.addFailureException(exception);
return;
}
if (runnable != null) {
runnable.run();

View File

@@ -151,14 +151,9 @@ public class SimpleStepFactoryBeanTests {
job.setSteps(Collections.singletonList(step));
JobExecution jobExecution = repository.createJobExecution(job.getName(), new JobParameters());
try {
job.execute(jobExecution);
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
// expected
assertEquals("Error!", e.getMessage());
}
job.execute(jobExecution);
assertEquals("Error!", jobExecution.getAllFailureExceptions().get(0).getMessage());
assertEquals(BatchStatus.FAILED, jobExecution.getStatus());
assertEquals(0, written.size());
@@ -180,14 +175,9 @@ public class SimpleStepFactoryBeanTests {
job.setSteps(Collections.singletonList((Step) step));
JobExecution jobExecution = repository.createJobExecution(job.getName(), new JobParameters());
try {
job.execute(jobExecution);
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
assertEquals("Foo", e.getMessage());
// expected
}
job.execute(jobExecution);
assertEquals("Foo", jobExecution.getAllFailureExceptions().get(0).getMessage());
assertEquals(BatchStatus.FAILED, jobExecution.getStatus());
}

View File

@@ -3,7 +3,6 @@ package org.springframework.batch.core.step.item;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
@@ -16,6 +15,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
@@ -24,8 +24,6 @@ import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepListener;
import org.springframework.batch.core.listener.SkipListenerSupport;
import org.springframework.batch.core.step.JobRepositorySupport;
import org.springframework.batch.core.step.skip.SkipLimitExceededException;
import org.springframework.batch.core.step.skip.SkipListenerFailedException;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
@@ -148,13 +146,8 @@ public class SkipLimitStepFactoryBeanTests {
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail();
}
catch (FatalRuntimeException expected) {
assertTrue(expected.getMessage().equals("Ouch!"));
}
step.execute(stepExecution);
assertTrue(stepExecution.getFailureExceptions().get(0).getMessage().equals("Ouch!"));
}
/**
@@ -169,12 +162,7 @@ public class SkipLimitStepFactoryBeanTests {
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail("Expected SkipLimitExceededException.");
}
catch (SkipLimitExceededException e) {
}
step.execute(stepExecution);
assertEquals(1, stepExecution.getSkipCount());
@@ -205,13 +193,9 @@ public class SkipLimitStepFactoryBeanTests {
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail("Expected SkipLimitExceededException.");
}
catch (SkipLimitExceededException e) {
}
step.execute(stepExecution);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
assertEquals(3, stepExecution.getSkipCount());
assertEquals(2, stepExecution.getReadSkipCount());
assertEquals(1, stepExecution.getWriteSkipCount());
@@ -249,13 +233,9 @@ public class SkipLimitStepFactoryBeanTests {
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail("Expected SkipListenerFailedException.");
}
catch (SkipListenerFailedException e) {
assertEquals("oops", e.getCause().getMessage());
}
step.execute(stepExecution);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
assertEquals("oops", stepExecution.getFailureExceptions().get(0).getCause().getMessage());
assertEquals(1, stepExecution.getSkipCount());
assertEquals(1, stepExecution.getReadSkipCount());
@@ -286,14 +266,9 @@ public class SkipLimitStepFactoryBeanTests {
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail("Expected SkipListenerFailedException.");
}
catch (SkipListenerFailedException e) {
assertEquals("oops", e.getCause().getMessage());
}
step.execute(stepExecution);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
assertEquals("oops", stepExecution.getFailureExceptions().get(0).getCause().getMessage());
assertEquals(3, stepExecution.getSkipCount());
assertEquals(2, stepExecution.getReadSkipCount());
assertEquals(1, stepExecution.getWriteSkipCount());
@@ -406,13 +381,8 @@ public class SkipLimitStepFactoryBeanTests {
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail("Expected SkipLimitExceededException.");
}
catch (SkipLimitExceededException e) {
}
step.execute(stepExecution);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
assertEquals("bad skip count", 3, stepExecution.getSkipCount());
assertEquals("bad read skip count", 2, stepExecution.getReadSkipCount());
assertEquals("bad write skip count", 1, stepExecution.getWriteSkipCount());

View File

@@ -29,6 +29,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
@@ -361,14 +362,9 @@ public class StatefulRetryStepFactoryBeanTests {
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail("Expected SkipLimitExceededException");
}
catch (SkipLimitExceededException e) {
// expected
}
step.execute(stepExecution);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
List<String> expectedOutput = Arrays.asList(StringUtils.commaDelimitedListToStringArray(""));
assertEquals(expectedOutput, written);
@@ -418,15 +414,9 @@ public class StatefulRetryStepFactoryBeanTests {
Step step = (Step) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
// expected
String message = e.getMessage();
assertTrue("Wrong message: " + message, message.contains("Write error - planned but not skippable."));
}
step.execute(stepExecution);
String message = stepExecution.getFailureExceptions().get(0).getMessage();
assertTrue("Wrong message: " + message, message.contains("Write error - planned but not skippable."));
List<String> expectedOutput = Arrays.asList(StringUtils.commaDelimitedListToStringArray(""));
assertEquals(expectedOutput, written);
@@ -466,14 +456,9 @@ public class StatefulRetryStepFactoryBeanTests {
AbstractStep step = (AbstractStep) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail("Expected SkipLimitExceededException");
}
catch (SkipLimitExceededException e) {
// expected
}
step.execute(stepExecution);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
List<String> expectedOutput = Arrays.asList(StringUtils.commaDelimitedListToStringArray(""));
assertEquals(expectedOutput, written);
@@ -519,14 +504,9 @@ public class StatefulRetryStepFactoryBeanTests {
AbstractStep step = (AbstractStep) factory.getObject();
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail("Expected RetryCacheCapacityExceededException");
}
catch (RetryCacheCapacityExceededException e) {
// expected
}
step.execute(stepExecution);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
// We added a bogus cache so no items are actually skipped
// because they aren't recognised as eligible
assertEquals(0, stepExecution.getSkipCount());

View File

@@ -0,0 +1,265 @@
/**
*
*/
package org.springframework.batch.core.step.item;
import static org.junit.Assert.*;
import static org.springframework.batch.core.BatchStatus.*;
import org.junit.Before;
import org.junit.Test;
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.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.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.ExitStatus;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.core.AttributeAccessor;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.DefaultTransactionStatus;
/**
* Tests for the behavior of TaskletStep in a failure scenario.
*
* @author Lucas Ward
*
*/
public class TaskletStepExceptionTests {
TaskletStep taskletStep;
StepExecution stepExecution;
UpdateCountingJobRepository jobRepository;
static RuntimeException taskletException = new RuntimeException();
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, new JobParameters(), "testJob" );
JobExecution jobExecution = new JobExecution(jobInstance);
stepExecution = new StepExecution("testStep", jobExecution);
}
@Test
public void testApplicationException() throws Exception{
taskletStep.execute(stepExecution);
assertEquals(FAILED, stepExecution.getStatus());
}
@Test
public void testInterrupted() throws Exception{
taskletStep.setStepExecutionListeners(new StepExecutionListener[]{new InterruptionListener()});
taskletStep.execute(stepExecution);
assertEquals(STOPPED, stepExecution.getStatus());
}
@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(1,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(1,jobRepository.getUpdateCount());
}
@Test
public void testAfterStepFailure() 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 ExitStatus execute(StepContribution contribution,
AttributeAccessor attributes) throws Exception {
return ExitStatus.FINISHED;
}
});
taskletStep.execute(stepExecution);
assertEquals(FAILED, stepExecution.getStatus());
assertTrue(stepExecution.getFailureExceptions().contains(exception));
assertEquals(2,jobRepository.getUpdateCount());
}
@Test
public void testOnErrorInStepFAilure() throws Exception{
final RuntimeException exception = new RuntimeException();
taskletStep.setStepExecutionListeners(new StepExecutionListenerSupport[]{new StepExecutionListenerSupport(){
@Override
public ExitStatus onErrorInStep(StepExecution stepExecution,
Throwable e) {
throw exception;
}
}});
taskletStep.execute(stepExecution);
assertEquals(FAILED, stepExecution.getStatus());
assertTrue(stepExecution.getFailureExceptions().contains(taskletException));
assertTrue(stepExecution.getFailureExceptions().contains(exception));
assertEquals(1,jobRepository.getUpdateCount());
}
@Test
public void testCloseError() throws Exception{
final RuntimeException exception = new RuntimeException();
taskletStep.setStreams(new ItemStream[]{new ItemStreamSupport(){
@Override
public void close(ExecutionContext executionContext)
throws ItemStreamException {
throw exception;
}
}});
taskletStep.execute(stepExecution);
assertEquals(FAILED, stepExecution.getStatus());
assertTrue(stepExecution.getFailureExceptions().contains(taskletException));
assertTrue(stepExecution.getFailureExceptions().contains(exception));
assertEquals(1,jobRepository.getUpdateCount());
}
@Test
public void testCommitError() throws Exception{
final RuntimeException exception = new RuntimeException();
taskletStep.setTransactionManager(new ResourcelessTransactionManager(){
@Override
protected void doCommit(DefaultTransactionStatus status)
throws TransactionException {
throw exception;
}
});
taskletStep.setTasklet(new Tasklet(){
public ExitStatus execute(StepContribution contribution,
AttributeAccessor attributes) throws Exception {
return ExitStatus.FINISHED;
}
});
taskletStep.execute(stepExecution);
assertEquals(UNKNOWN, stepExecution.getStatus());
Throwable e = stepExecution.getFailureExceptions().get(0);
assertEquals(exception, e.getCause());
}
@Test
public void testUpdateError() throws Exception{
final RuntimeException exception = new RuntimeException();
taskletStep.setJobRepository(new UpdateCountingJobRepository(){
@Override
public void update(StepExecution arg0) {
throw exception;
}
});
taskletStep.execute(stepExecution);
assertEquals(UNKNOWN, stepExecution.getStatus());
assertTrue(stepExecution.getFailureExceptions().contains(taskletException));
assertTrue(stepExecution.getFailureExceptions().contains(exception));
}
private static class ExceptionTasklet implements Tasklet{
public ExitStatus execute(StepContribution contribution,
AttributeAccessor attributes) 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;
public void add(StepExecution stepExecution) { }
public JobExecution createJobExecution(String jobName,
JobParameters jobParameters)
throws JobExecutionAlreadyRunningException,
JobRestartException, JobInstanceAlreadyCompleteException {
return null;}
public StepExecution getLastStepExecution(JobInstance jobInstance,
String stepName) {return null;}
public int getStepExecutionCount(JobInstance jobInstance,
String stepName) {return 0; }
public boolean isJobInstanceExists(String jobName,
JobParameters jobParameters) {return false;}
public void update(JobExecution jobExecution) { }
public void update(StepExecution stepExecution) {
updateCount++;
}
public void updateExecutionContext(StepExecution stepExecution) { }
public int getUpdateCount() {
return updateCount;
}
}
}

View File

@@ -18,7 +18,6 @@ package org.springframework.batch.core.step.tasklet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
@@ -39,7 +38,6 @@ 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.support.JobRepositoryFactoryBean;
import org.springframework.batch.core.step.tasklet.TaskletStep;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
@@ -134,27 +132,20 @@ public class ChunkOrientedStepIntegrationTests {
});
// step.setLastExecution(stepExecution);
try {
step.execute(stepExecution);
fail("Expected BatchCriticalException");
}
catch (RuntimeException e) {
step.execute(stepExecution);
assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus());
StepExecution lastStepExecution = jobRepository.getLastStepExecution(jobExecution.getJobInstance(), step.getName());
assertEquals(lastStepExecution, stepExecution);
assertFalse(lastStepExecution == stepExecution);
assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus());
StepExecution lastStepExecution = jobRepository.getLastStepExecution(jobExecution.getJobInstance(), step.getName());
assertEquals(lastStepExecution, stepExecution);
assertFalse(lastStepExecution == stepExecution);
// If the StepExecution is not saved after the failure it will be
// STARTED instead of UNKNOWN
assertEquals(BatchStatus.UNKNOWN, lastStepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertTrue(msg.contains("Fatal error detected during commit"));
// The original rollback was caused by this one:
assertEquals("Simulate commit failure", stepExecution.getFailureExceptions().get(0).getCause().getMessage());
// If the StepExecution is not saved after the failure it will be
// STARTED instead of UNKNOWN
assertEquals(BatchStatus.UNKNOWN, lastStepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertTrue(msg.contains("Fatal error detected during commit"));
// The original rollback was caused by this one:
assertEquals("Simulate commit failure", e.getCause().getMessage());
}
}
}

View File

@@ -169,15 +169,10 @@ public class StepExecutorInterruptionTests extends TestCase {
}
});
try {
step.execute(stepExecution);
fail("Expected planned RuntimeException");
} catch (RuntimeException e) {
assertEquals("Planned!", e.getMessage());
}
step.execute(stepExecution);
assertEquals("Planned!", stepExecution.getFailureExceptions().get(0).getMessage());
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
}
/**

View File

@@ -36,7 +36,6 @@ import org.springframework.batch.core.JobInterruptedException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.job.JobSupport;
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
import org.springframework.batch.core.repository.JobRepository;
@@ -172,16 +171,8 @@ public class TaskletStepTests {
step.setJobRepository(repository);
step.afterPropertiesSet();
try {
step.execute(stepExecution);
fail();
}
catch (Exception e) {
assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus());
assertEquals("Fatal error detected during update of step execution", e.getMessage());
assertEquals("stub exception", e.getCause().getMessage());
}
step.execute(stepExecution);
assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus());
}
@Test
@@ -328,14 +319,11 @@ public class TaskletStepTests {
counter++;
}
});
try {
step.execute(stepExecution);
fail();
}
catch (RuntimeException e) {
assertEquals("Fatal error detected during save of step execution context", e.getMessage());
assertEquals("foo", e.getCause().getMessage());
}
step.execute(stepExecution);
Throwable e = stepExecution.getFailureExceptions().get(0);
assertEquals("Fatal error detected during save of step execution context", e.getMessage());
assertEquals("foo", e.getCause().getMessage());
assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus());
}
@@ -497,13 +485,8 @@ public class TaskletStepTests {
}, itemWriter));
JobExecution jobExecution = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step.getName(), jobExecution);
try {
step.execute(stepExecution);
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
assertEquals("FOO", e.getMessage());
}
step.execute(stepExecution);
assertEquals("FOO", stepExecution.getFailureExceptions().get(0).getMessage());
assertEquals(1, list.size());
}
@@ -533,7 +516,7 @@ public class TaskletStepTests {
}
@Test
public void testStatusForInterruptedException() {
public void testStatusForInterruptedException() throws Exception{
StepInterruptionPolicy interruptionPolicy = new StepInterruptionPolicy() {
@@ -560,16 +543,11 @@ public class TaskletStepTests {
stepExecution.setExecutionContext(foobarEc);
try {
step.execute(stepExecution);
fail("Expected JobInterruptedException");
}
catch (JobInterruptedException ex) {
assertEquals(BatchStatus.STOPPED, stepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertTrue("Message does not contain 'JobInterruptedException': " + msg, contains(msg,
step.execute(stepExecution);
assertEquals(BatchStatus.STOPPED, stepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertTrue("Message does not contain 'JobInterruptedException': " + msg, contains(msg,
"JobInterruptedException"));
}
}
@Test
@@ -589,15 +567,10 @@ public class TaskletStepTests {
stepExecution.setExecutionContext(foobarEc);
// step.setLastExecution(stepExecution);
try {
step.execute(stepExecution);
fail("Expected RuntimeException");
}
catch (RuntimeException ex) {
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
// The original rollback was caused by this one:
assertEquals("Foo", ex.getMessage());
}
step.execute(stepExecution);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
// The original rollback was caused by this one:
assertEquals("Foo", stepExecution.getFailureExceptions().get(0).getMessage());
}
@Test
@@ -617,15 +590,10 @@ public class TaskletStepTests {
stepExecution.setExecutionContext(foobarEc);
// step.setLastExecution(stepExecution);
try {
step.execute(stepExecution);
fail("Expected Error");
}
catch (Error ex) {
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
// The original rollback was caused by this one:
assertEquals("Foo", ex.getMessage());
}
step.execute(stepExecution);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
// The original rollback was caused by this one:
assertEquals("Foo", stepExecution.getFailureExceptions().get(0).getMessage());
}
@Test
@@ -651,17 +619,12 @@ public class TaskletStepTests {
stepExecution.setExecutionContext(foobarEc);
// step.setLastExecution(stepExecution);
try {
step.execute(stepExecution);
fail("Expected UnexpectedJobExecutionException");
}
catch (RuntimeException ex) {
assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertTrue("Message does not contain ResetFailedException: " + msg, contains(msg, "ResetFailedException"));
// The original rollback was caused by this one:
assertEquals("Bar", ex.getCause().getMessage());
}
step.execute(stepExecution);
assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertTrue("Message does not contain ResetFailedException: " + msg, contains(msg, "ResetFailedException"));
// The original rollback was caused by this one:
assertEquals("Bar", stepExecution.getFailureExceptions().get(0).getCause().getMessage());
}
@Test
@@ -680,19 +643,15 @@ public class TaskletStepTests {
stepExecution.setExecutionContext(foobarEc);
// step.setLastExecution(stepExecution);
try {
step.execute(stepExecution);
fail("Expected BatchCriticalException");
}
catch (RuntimeException ex) {
assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertTrue(msg.contains("Fatal error detected during commit"));
msg = ex.getMessage();
assertTrue(msg.contains("Fatal error detected during commit"));
// The original rollback was caused by this one:
assertEquals("Bar", ex.getCause().getMessage());
}
step.execute(stepExecution);
assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertTrue(msg.contains("Fatal error detected during commit"));
Throwable ex = stepExecution.getFailureExceptions().get(0);
msg = ex.getMessage();
assertTrue(msg.contains("Fatal error detected during commit"));
// The original rollback was caused by this one:
assertEquals("Bar", ex.getCause().getMessage());
}
@Test
@@ -708,20 +667,15 @@ public class TaskletStepTests {
JobExecution jobExecutionContext = new JobExecution(jobInstance);
StepExecution stepExecution = new StepExecution(step.getName(), jobExecutionContext);
try {
step.execute(stepExecution);
fail("Expected RuntimeException");
}
catch (RuntimeException ex) {
// The job actually completed, but the streams couldn't be closed.
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertEquals("", msg);
msg = ex.getMessage();
assertTrue("Message does not contain 'closing step': " + msg, contains(msg, "closing step"));
// The original rollback was caused by this one:
assertEquals("Bar", ex.getCause().getMessage());
}
step.execute(stepExecution);
// The job actually completed, but the streams couldn't be closed.
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertEquals("", msg);
Throwable ex = stepExecution.getFailureExceptions().get(0);
msg = ex.getMessage();
// The original rollback was caused by this one:
assertEquals("Bar", ex.getMessage());
}
@Test
@@ -743,20 +697,15 @@ public class TaskletStepTests {
stepExecution.setExecutionContext(foobarEc);
// step.setLastExecution(stepExecution);
try {
step.execute(stepExecution);
fail("Expected InfrastructureException");
}
catch (UnexpectedJobExecutionException ex) {
step.execute(stepExecution);
// The job actually completed, but the streams couldn't be closed.
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertEquals("", msg);
msg = ex.getMessage();
assertTrue("Message does not contain 'closing': " + msg, contains(msg, "closing"));
// The original rollback was caused by this one:
assertEquals("Bar", ex.getCause().getMessage());
}
assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus());
String msg = stepExecution.getExitStatus().getExitDescription();
assertEquals("", msg);
Throwable ex = stepExecution.getFailureExceptions().get(0);
msg = ex.getMessage();
// The original rollback was caused by this one:
assertEquals("Bar", ex.getMessage());
}
/**
@@ -777,16 +726,12 @@ public class TaskletStepTests {
StepExecution stepExecution = new StepExecution(step.getName(), new JobExecution(jobInstance));
try {
step.execute(stepExecution);
fail("Expected InfrastructureException");
}
catch (RuntimeException expected) {
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
assertEquals("CRASH!", expected.getMessage());
assertFalse(stepExecution.getExecutionContext().isEmpty());
assertTrue(stepExecution.getExecutionContext().getString("spam").equals("bucket"));
}
step.execute(stepExecution);
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
Throwable expected = stepExecution.getFailureExceptions().get(0);
assertEquals("CRASH!", expected.getMessage());
assertFalse(stepExecution.getExecutionContext().isEmpty());
assertTrue(stepExecution.getExecutionContext().getString("spam").equals("bucket"));
}
@Test
@@ -820,14 +765,10 @@ public class TaskletStepTests {
};
step.setStepExecutionListeners(new StepExecutionListener[] { listener });
StepExecution stepExecution = new StepExecution(step.getName(), new JobExecution(jobInstance));
try {
step.execute(stepExecution);
fail();
}
catch (RuntimeException expected) {
assertEquals("exception thrown in afterStep to signal failure", expected.getMessage());
}
step.execute(stepExecution);
Throwable expected = stepExecution.getFailureExceptions().get(0);
assertEquals("exception thrown in afterStep to signal failure", expected.getMessage());
assertEquals(BatchStatus.FAILED, stepExecution.getStatus());
}