diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/ExceptionThrowingItemHandlerStub.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/AbstractExceptionThrowingItemHandlerStub.java similarity index 64% rename from spring-batch-core/src/test/java/org/springframework/batch/core/step/item/ExceptionThrowingItemHandlerStub.java rename to spring-batch-core/src/test/java/org/springframework/batch/core/step/item/AbstractExceptionThrowingItemHandlerStub.java index 7c4846ce0..f9d9dd4f8 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/ExceptionThrowingItemHandlerStub.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/AbstractExceptionThrowingItemHandlerStub.java @@ -15,6 +15,8 @@ */ package org.springframework.batch.core.step.item; +import java.lang.reflect.Constructor; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -26,34 +28,33 @@ import org.apache.commons.logging.LogFactory; * @author Dan Garrette * @since 2.0.1 */ -public abstract class ExceptionThrowingItemHandlerStub { - +public abstract class AbstractExceptionThrowingItemHandlerStub { + protected Log logger = LogFactory.getLog(getClass()); private Collection failures = Collections.emptyList(); - private boolean runtimeException = false; + private Constructor exception; + + public AbstractExceptionThrowingItemHandlerStub() throws Exception { + exception = SkippableRuntimeException.class.getConstructor(String.class); + } public void setFailures(T... failures) { - this.failures = Arrays.asList(failures); + this.failures = new ArrayList(Arrays.asList(failures)); } - public void setRuntimeException(boolean runtimeException) { - this.runtimeException = runtimeException; + public void setExceptionType(Class exceptionType) throws Exception { + exception = exceptionType.getConstructor(String.class); } - public void clear() { + public void clearFailures() { failures.clear(); } protected void checkFailure(T item) throws Exception { if (isFailure(item)) { - if (runtimeException) { - throw new SkippableRuntimeException("Intended Failure: "+item); - } - else { - throw new SkippableException("Intended Failure: "+item); - } + throw exception.newInstance("Intended Failure: " + item); } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FatalException.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FatalException.java new file mode 100644 index 000000000..cc9c7b663 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FatalException.java @@ -0,0 +1,11 @@ +package org.springframework.batch.core.step.item; + +/** + * @author Dan Garrette + * @since 2.0.2 + */ +public class FatalException extends SkippableException { + public FatalException(String message) { + super(message); + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FatalRuntimeException.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FatalRuntimeException.java new file mode 100644 index 000000000..730732142 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FatalRuntimeException.java @@ -0,0 +1,11 @@ +package org.springframework.batch.core.step.item; + +/** + * @author Dan Garrette + * @since 2.0.2 + */ +public class FatalRuntimeException extends SkippableRuntimeException { + public FatalRuntimeException(String message) { + super(message); + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests.java new file mode 100644 index 000000000..3ae614ae9 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests.java @@ -0,0 +1,204 @@ +package org.springframework.batch.core.step.item; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.job.SimpleJob; +import org.springframework.batch.core.launch.JobLauncher; +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.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @author Dan Garrette + * @since 2.0.2 + */ +public class FaultTolerantExceptionClassesTests { + + // + // TODO BATCH-1318: Commented out tests are related to this issue + // + + private static ApplicationContext ctx; + + private static JobRepository jobRepository; + + private static JobLauncher jobLauncher; + + private static SkipReaderStub reader; + + private static SkipWriterStub writer; + + @SuppressWarnings("unchecked") + @BeforeClass + public static void setCtx() { + ctx = new ClassPathXmlApplicationContext( + "/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests-context.xml"); + jobRepository = (JobRepository) ctx.getBean("jobRepository"); + jobLauncher = (JobLauncher) ctx.getBean("jobLauncher"); + reader = (SkipReaderStub) ctx.getBean("reader"); + writer = (SkipWriterStub) ctx.getBean("writer"); + } + + @Before + public void setup() { + reader.clear(); + writer.clear(); + } + + @Test + public void testNonSkippable() throws Exception { + writer.setExceptionType(RuntimeException.class); + StepExecution stepExecution = launchStep("nonSkippableStep"); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + assertEquals("[1, 2, 3]", writer.getWritten().toString()); + assertEquals("[]", writer.getCommitted().toString()); + } + + @Test + public void testNonSkippableChecked() throws Exception { + writer.setExceptionType(Exception.class); + StepExecution stepExecution = launchStep("nonSkippableStep"); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + assertEquals("[1, 2, 3]", writer.getWritten().toString()); + assertEquals("[]", writer.getCommitted().toString()); + } + + @Test + public void testDefaultFatal() throws Exception { + writer.setExceptionType(RuntimeException.class); + StepExecution stepExecution = launchStep("skippableStep"); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + // TODO BATCH-1318: assertEquals("[1, 2, 3]", writer.getWritten().toString()); + // TODO BATCH-1318: assertEquals("[]", writer.getCommitted().toString()); + } + + @Test + public void testSkippable() throws Exception { + writer.setExceptionType(SkippableRuntimeException.class); + StepExecution stepExecution = launchStep("skippableStep"); + assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus()); + assertEquals("[1, 2, 3, 1, 2, 3, 4]", writer.getWritten().toString()); + assertEquals("[1, 2, 4]", writer.getCommitted().toString()); + } + + @Test + public void testFatal() throws Exception { + writer.setExceptionType(FatalRuntimeException.class); + StepExecution stepExecution = launchStep("skippableStep"); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + assertEquals("[1, 2, 3]", writer.getWritten().toString()); + assertEquals("[]", writer.getCommitted().toString()); + } + + @Test + public void testDefaultFatalChecked() throws Exception { + writer.setExceptionType(Exception.class); + StepExecution stepExecution = launchStep("skippableStep"); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + // TODO BATCH-1318: assertEquals("[1, 2, 3]", writer.getWritten().toString()); + // TODO BATCH-1318: assertEquals("[]", writer.getCommitted().toString()); + } + + @Test + public void testSkippableChecked() throws Exception { + writer.setExceptionType(SkippableException.class); + StepExecution stepExecution = launchStep("skippableStep"); + // TODO BATCH-1318: assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus()); + // TODO BATCH-1318: assertEquals("[1, 2, 3, 1, 2, 3, 4]", writer.getWritten().toString()); + // TODO BATCH-1318: assertEquals("[1, 2, 4]", writer.getCommitted().toString()); + } + + @Test + public void testFatalChecked() throws Exception { + writer.setExceptionType(FatalException.class); + StepExecution stepExecution = launchStep("skippableStep"); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + // TODO BATCH-1318: assertEquals("[1, 2, 3]", writer.getWritten().toString()); + // TODO BATCH-1318: assertEquals("[]", writer.getCommitted().toString()); + } + + @Test + public void testRetryableDefaultFatal() throws Exception { + writer.setExceptionType(RuntimeException.class); + StepExecution stepExecution = launchStep("retryable"); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + assertEquals("[1, 2, 3, 1, 2, 3, 1, 2, 3]", writer.getWritten().toString()); + // TODO BATCH-1318: assertEquals("[]", writer.getCommitted().toString()); + } + + @Test + public void testRetryableSkippable() throws Exception { + writer.setExceptionType(SkippableRuntimeException.class); + StepExecution stepExecution = launchStep("retryable"); + assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus()); + assertEquals("[1, 2, 3, 1, 2, 3, 1, 2, 3, 4]", writer.getWritten().toString()); + assertEquals("[1, 2, 4]", writer.getCommitted().toString()); + } + + @Test + public void testRetryableFatal() throws Exception { + writer.setExceptionType(FatalRuntimeException.class); + StepExecution stepExecution = launchStep("retryable"); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + // TODO BATCH-1318: assertEquals("[1, 2, 3, 1, 2, 3, 1, 2, 3]", writer.getWritten().toString()); + assertEquals("[]", writer.getCommitted().toString()); + } + + @Test + public void testRetryableDefaultFatalChecked() throws Exception { + writer.setExceptionType(Exception.class); + StepExecution stepExecution = launchStep("retryable"); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + assertEquals("[1, 2, 3, 1, 2, 3, 1, 2, 3]", writer.getWritten().toString()); + // TODO BATCH-1318: assertEquals("[]", writer.getCommitted().toString()); + } + + @Test + public void testRetryableSkippableChecked() throws Exception { + writer.setExceptionType(SkippableException.class); + StepExecution stepExecution = launchStep("retryable"); + // TODO BATCH-1318: assertEquals(BatchStatus.COMPLETED, stepExecution.getStatus()); + // TODO BATCH-1318: assertEquals("[1, 2, 3, 1, 2, 3, 1, 2, 3, 4]", writer.getWritten().toString()); + // TODO BATCH-1318: assertEquals("[1, 2, 4]", writer.getCommitted().toString()); + } + + @Test + public void testRetryableFatalChecked() throws Exception { + writer.setExceptionType(FatalException.class); + StepExecution stepExecution = launchStep("retryable"); + assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); + assertEquals("[1, 2, 3, 1, 2, 3, 1, 2, 3]", writer.getWritten().toString()); + // TODO BATCH-1318: assertEquals("[]", writer.getCommitted().toString()); + } + + private StepExecution launchStep(String stepName) throws JobExecutionAlreadyRunningException, JobRestartException, + JobInstanceAlreadyCompleteException { + SimpleJob job = new SimpleJob(); + job.setName("job"); + job.setJobRepository(jobRepository); + + List stepsToExecute = new ArrayList(); + stepsToExecute.add((Step) ctx.getBean(stepName)); + job.setSteps(stepsToExecute); + + JobExecution jobExecution = jobLauncher.run(job, new JobParametersBuilder().addLong("timestamp", + new Date().getTime()).toJobParameters()); + return jobExecution.getStepExecutions().iterator().next(); + } + +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRollbackTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRollbackTests.java index 4557798c1..700adab37 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRollbackTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanRollbackTests.java @@ -37,11 +37,11 @@ public class FaultTolerantStepFactoryBeanRollbackTests { private FaultTolerantStepFactoryBean factory; - private SkipReaderStub reader = new SkipReaderStub(); + private SkipReaderStub reader; - private SkipProcessorStub processor = new SkipProcessorStub(); + private SkipProcessorStub processor; - private SkipWriterStub writer = new SkipWriterStub(); + private SkipWriterStub writer; private JobExecution jobExecution; @@ -49,6 +49,12 @@ public class FaultTolerantStepFactoryBeanRollbackTests { private JobRepository repository; + public FaultTolerantStepFactoryBeanRollbackTests() throws Exception { + reader = new SkipReaderStub(); + processor = new SkipProcessorStub(); + writer = new SkipWriterStub(); + } + @Before public void setUp() throws Exception { factory = new FaultTolerantStepFactoryBean(); @@ -118,7 +124,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { public void testReaderDefaultNoRollbackOnCheckedException() throws Exception { reader.setItems("1", "2", "3", "4"); reader.setFailures("2", "3"); - reader.setRuntimeException(false); + reader.setExceptionType(SkippableException.class); Step step = (Step) factory.getObject(); @@ -135,7 +141,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { public void testReaderAttributesOverrideSkippableNoRollback() throws Exception { reader.setFailures("2", "3"); reader.setItems("1", "2", "3", "4"); - reader.setRuntimeException(false); + reader.setExceptionType(SkippableException.class); // No skips by default factory.setSkippableExceptionClasses(new HashSet>()); @@ -159,7 +165,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { reader.setItems("1", "2", "3", "4"); processor.setFailures("1", "3"); - processor.setRuntimeException(false); + processor.setExceptionType(SkippableException.class); Step step = (Step) factory.getObject(); @@ -177,7 +183,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { reader.setItems("1", "2", "3", "4"); processor.setFailures("1", "3"); - processor.setRuntimeException(true); + processor.setExceptionType(SkippableRuntimeException.class); Step step = (Step) factory.getObject(); @@ -190,7 +196,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { @Test public void testProcessSkipWithNoRollbackForCheckedException() throws Exception { processor.setFailures("4"); - processor.setRuntimeException(false); + processor.setExceptionType(SkippableException.class); factory.setNoRollbackExceptionClasses(getExceptionList(SkippableException.class)); @@ -220,7 +226,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { @Test public void testWriterDefaultRollbackOnCheckedException() throws Exception { writer.setFailures("2", "3"); - writer.setRuntimeException(false); + writer.setExceptionType(SkippableException.class); Step step = (Step) factory.getObject(); @@ -236,7 +242,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { @Test public void testWriterDefaultRollbackOnRuntimeException() throws Exception { writer.setFailures("2", "3"); - writer.setRuntimeException(true); + writer.setExceptionType(SkippableRuntimeException.class); Step step = (Step) factory.getObject(); @@ -253,7 +259,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { @Test public void testWriterNoRollbackOnRuntimeException() throws Exception { writer.setFailures("2", "3"); - writer.setRuntimeException(true); + writer.setExceptionType(SkippableRuntimeException.class); factory.setNoRollbackExceptionClasses(getExceptionList(SkippableRuntimeException.class)); @@ -274,7 +280,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { @Test public void testWriterNoRollbackOnCheckedException() throws Exception { writer.setFailures("2", "3"); - writer.setRuntimeException(false); + writer.setExceptionType(SkippableException.class); factory.setNoRollbackExceptionClasses(getExceptionList(SkippableException.class)); @@ -352,8 +358,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { assertEquals("[1, 2, 3, 5]", writer.getCommitted().toString()); assertEquals("[1, 2, 3, 4, 1, 2, 3, 4, 5]", writer.getWritten().toString()); // TODO: Fix this with BATCH-1259? - assertEquals("[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]", processor.getProcessed() - .toString()); + assertEquals("[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]", processor.getProcessed().toString()); } @Test @@ -387,8 +392,7 @@ public class FaultTolerantStepFactoryBeanRollbackTests { assertEquals("[1, 2, 1, 2, 3, 4, 5]", writer.getWritten().toString()); assertEquals("[1, 3, 5]", processor.getCommitted().toString()); // TODO: Fix this with BATCH-1259? - assertEquals("[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]", processor.getProcessed() - .toString()); + assertEquals("[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]", processor.getProcessed().toString()); } @SuppressWarnings("unchecked") diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java index b1ab20c4d..a1eb35821 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/FaultTolerantStepFactoryBeanTests.java @@ -59,11 +59,11 @@ public class FaultTolerantStepFactoryBeanTests { private FaultTolerantStepFactoryBean factory; - private SkipReaderStub reader = new SkipReaderStub(); + private SkipReaderStub reader; - private SkipProcessorStub processor = new SkipProcessorStub();; + private SkipProcessorStub processor; - private SkipWriterStub writer = new SkipWriterStub(); + private SkipWriterStub writer; private JobExecution jobExecution; @@ -75,6 +75,12 @@ public class FaultTolerantStepFactoryBeanTests { private boolean closed = false; + public FaultTolerantStepFactoryBeanTests() throws Exception { + reader = new SkipReaderStub(); + processor = new SkipProcessorStub(); + writer = new SkipWriterStub(); + } + @Before public void setUp() throws Exception { factory = new FaultTolerantStepFactoryBean(); @@ -786,12 +792,6 @@ public class FaultTolerantStepFactoryBeanTests { } } - private static class FatalRuntimeException extends SkippableRuntimeException { - public FatalRuntimeException(String message) { - super(message); - } - } - private void assertStepExecutionsAreEqual(StepExecution expected, StepExecution actual) { assertEquals(expected.getId(), actual.getId()); assertEquals(expected.getStartTime(), actual.getStartTime()); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipProcessorStub.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipProcessorStub.java index 84a19a1ba..b2d960c5b 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipProcessorStub.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipProcessorStub.java @@ -25,7 +25,7 @@ import org.springframework.batch.support.transaction.TransactionAwareProxyFactor * @author Dan Garrette * @since 2.0.1 */ -public class SkipProcessorStub extends ExceptionThrowingItemHandlerStub implements ItemProcessor { +public class SkipProcessorStub extends AbstractExceptionThrowingItemHandlerStub implements ItemProcessor { private List processed = new ArrayList(); @@ -33,6 +33,10 @@ public class SkipProcessorStub extends ExceptionThrowingItemHandlerStub im private boolean filter = false; + public SkipProcessorStub() throws Exception { + super(); + } + public List getProcessed() { return processed; } @@ -46,7 +50,6 @@ public class SkipProcessorStub extends ExceptionThrowingItemHandlerStub im } public void clear() { - super.clear(); processed.clear(); committed.clear(); filter = false; diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipReaderStub.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipReaderStub.java index 1ed197543..9c3bf9257 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipReaderStub.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipReaderStub.java @@ -27,7 +27,7 @@ import org.springframework.util.Assert; * @author Dan Garrette * @since 2.0.1 */ -public class SkipReaderStub extends ExceptionThrowingItemHandlerStub implements ItemReader { +public class SkipReaderStub extends AbstractExceptionThrowingItemHandlerStub implements ItemReader { private T[] items; @@ -35,10 +35,12 @@ public class SkipReaderStub extends ExceptionThrowingItemHandlerStub imple private int counter = -1; - public SkipReaderStub() { + public SkipReaderStub() throws Exception { + super(); } - public SkipReaderStub(T... items) { + public SkipReaderStub(T... items) throws Exception { + super(); this.items = items; } @@ -52,7 +54,6 @@ public class SkipReaderStub extends ExceptionThrowingItemHandlerStub imple } public void clear() { - super.clear(); counter = -1; read.clear(); } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipWriterStub.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipWriterStub.java index 06df81f5f..20aab5b0d 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipWriterStub.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/item/SkipWriterStub.java @@ -25,12 +25,16 @@ import org.springframework.batch.support.transaction.TransactionAwareProxyFactor * @author Dan Garrette * @since 2.0.1 */ -public class SkipWriterStub extends ExceptionThrowingItemHandlerStub implements ItemWriter { - +public class SkipWriterStub extends AbstractExceptionThrowingItemHandlerStub implements ItemWriter { + private List written = new ArrayList(); private List committed = TransactionAwareProxyFactory.createTransactionalList(); + public SkipWriterStub() throws Exception { + super(); + } + public List getWritten() { return written; } @@ -40,13 +44,12 @@ public class SkipWriterStub extends ExceptionThrowingItemHandlerStub imple } public void clear() { - super.clear(); written.clear(); committed.clear(); } public void write(List items) throws Exception { - logger.debug("Writing: "+items); + logger.debug("Writing: " + items); for (T item : items) { written.add(item); checkFailure(item); diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests-context.xml new file mode 100644 index 000000000..4df98e77f --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/step/item/FaultTolerantExceptionClassesTests-context.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + org.springframework.batch.core.step.item.SkippableRuntimeException + + + org.springframework.batch.core.step.item.FatalRuntimeException + + + + + + + + + + org.springframework.batch.core.step.item.SkippableRuntimeException + + + org.springframework.batch.core.step.item.FatalRuntimeException + + + java.lang.Exception + + + + + + + + + 1 + 2 + 3 + 4 + + + + + + + + 3 + + + + + + + + + + + + + + + \ No newline at end of file