Excluding exceptions from skipping

In my previous posts under Spring Batch, I showed that when reading, processing or writing records in a batch, exceptions can be thrown, we can skip them and move on to next record. This is done with xml configuration as shown below


1 	<batch:job id="importEmployees">
2 		<batch:step id="readWriteEmployees">
3 			<batch:tasklet>
4 				<batch:chunk reader="reader" writer="writer" commit-interval="50" skip-limit="200">
5 					<batch:skippable-exception-classes>
6 						<batch:include class="org.springframework.batch.item.file.FlatFileParseException"/>
7 					</batch:skippable-exception-classes>
8 				</batch:chunk>
9 			</batch:tasklet>
10		</batch:step>
11	</batch:job>

In the above xml configuration, at line 5 I have added “skippable-exception-classes” element and in that I have mentioned the exception class that has to be skipped by using the “include” element. We set the class attribute of “include” element with the fully qualified class name of FlatFileParseException.

In this way we are telling the Spring Batch that when processing records in a batch. If any of the record is throwing FlatFileParseException, skip that record and move to next record.

But they are some exceptions which we cannot skip. When those kind of exceptions happen, the Spring Batch has to stop processing records.

To mention the exception that should not be skipped by Spring batch, we use “exclude” element as show in the below xml configuration.


1 	<batch:job id="importEmployees">
2 		<batch:step id="readWriteEmployees">
3 			<batch:tasklet>
4 				<batch:chunk reader="reader" writer="writer" commit-interval="50" skip-limit="200" processor="exceptionSimulator" >
5 					<batch:skippable-exception-classes>
6 						<batch:include class="java.lang.IllegalArgumentException"/>
7 						<batch:exclude class="java.lang.NullPointerException"/>
8 					</batch:skippable-exception-classes>
9 				</batch:chunk>
10			</batch:tasklet>
11		</batch:step>
12	</batch:job>

In the above xml configuration, at line 5 we have added “skippable-exception-classes” element and at line 6 we have added “include” element similar to previous example.

Now at line 7, we have added a new element named “exclude” and set the class attribute to the fully qualified class name of NullPointerException.

We are telling Spring Batch to not skip records when NullPointerException is thrown. The movement NullPointerException is thrown, Spring Batch will stop processing.

Most of the time exclude element is used to exclude an exception which is subclass of an exception mentioned in the include element.

The thing is include element skips a complete exception hierarchy but if we don’t want a particular subclass exception in the complete exception hierarchy to be skipped, we can use exclude element here.

For my example I have created an ExceptionSimulator class which throws exception. Below is the complete code

ExceptionSimulator


1  package package30;
2  
3  import org.springframework.batch.item.ItemProcessor;
4  
5  public class ExceptionSimulator implements ItemProcessor {
6  	int count = 0;
7  	
8  	@Override
9  	public Employee process(Employee employee) throws Exception {
10 		Employee employee1 = null;
11 		
12 		if(count == 9) {
13 			throw new NullPointerException();
14 		} else if (count % 2 == 0) {
15 			System.out.println("Processsing record: " + count);
16 			employee1 = employee;
17 			count = count + 1;
18 		} else if (count %2 == 1) {
19 			System.out.println("Throwing IllegalArgumentException: " + count);
20 			count = count + 1;
21 			throw new IllegalArgumentException();
22 		}
23 		return employee1;
24 	}
25 }

Since the ExceptionSimulator class implements ItemProcessor, the exceptions are thrown when processing the records.

According to the code, when reading employee record positioned at every odd line, IllegalArgumentException is thrown and at every even positioned employee record, the same data is returned as it is. At 9th record NullPointerException is thrown.

Below is the complete xml configuration for your reference


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:batch="http://www.springframework.org/schema/batch"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd">
	
	<bean id="employee" class="package30.Employee" scope="prototype"/>
	
	<bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
		<property name="resource" value="file:FileInput.txt"/>
		<property name="lineMapper">
			<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
				<property name="lineTokenizer">
					<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
						<property name="names" value="id,name,status,salary"/>
					</bean>
				</property>
				<property name="fieldSetMapper">
					<bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
						<property name="prototypeBeanName" value="employee"/>
					</bean>
				</property>
			</bean>
		</property> 
	</bean>
	
	<bean id="writer" class="org.springframework.batch.item.file.FlatFileItemWriter">
		<property name="resource" value="file:FileOutput.txt"/>
		<property name="lineAggregator">
			<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
				<property name="fieldExtractor">
					<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
						<property name="names" value="id,name,status,salary"/>
					</bean>
				</property>
			</bean>
		</property>
	</bean>
	
	<bean id="exceptionSimulator" class="package30.ExceptionSimulator" />
	
	<batch:job id="importEmployees">
		<batch:step id="readWriteEmployees">
			<batch:tasklet>
				<batch:chunk reader="reader" writer="writer" commit-interval="50" skip-limit="200" processor="exceptionSimulator" >
					<batch:skippable-exception-classes>
						<batch:include class="java.lang.IllegalArgumentException"/>
						<batch:exclude class="java.lang.NullPointerException"/>
					</batch:skippable-exception-classes>
				</batch:chunk>
			</batch:tasklet>
		</batch:step>
	</batch:job>
	
	<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
	
	<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
		<property name="transactionManager" ref="transactionManager"/>
	</bean>
	
	<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
		<property name="jobRepository" ref="jobRepository"/>
	</bean>
</beans>

So when I run the example, below is the output generated.

Output

Apr 11, 2020 10:42:46 AM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5b80350b: startup date [Sat Apr 11 10:42:46 IST 2020]; root of context hierarchy
Apr 11, 2020 10:42:46 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [package30/job.xml]
Apr 11, 2020 10:42:47 AM org.springframework.batch.core.launch.support.SimpleJobLauncher afterPropertiesSet
INFO: No TaskExecutor has been set, defaulting to synchronous executor.
Apr 11, 2020 10:42:47 AM org.springframework.batch.core.launch.support.SimpleJobLauncher run
INFO: Job: [FlowJob: [name=importEmployees]] launched with the following parameters: [{date=1586581967518}]
Apr 11, 2020 10:42:47 AM org.springframework.batch.core.job.SimpleStepHandler handleStep
INFO: Executing step: [readWriteEmployees]
Processsing record: 0
Throwing IllegalArgumentException: 1
Processsing record: 2
Throwing IllegalArgumentException: 3
Processsing record: 4
Throwing IllegalArgumentException: 5
Processsing record: 6
Throwing IllegalArgumentException: 7
Processsing record: 8
Apr 11, 2020 10:42:47 AM org.springframework.batch.core.step.AbstractStep execute
SEVERE: Encountered an error executing step readWriteEmployees in job importEmployees
org.springframework.retry.RetryException: Non-skippable exception in recoverer while processing; nested exception is java.lang.NullPointerException
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor$2.recover(FaultTolerantChunkProcessor.java:283)
at org.springframework.retry.support.RetryTemplate.handleRetryExhausted(RetryTemplate.java:512)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:351)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:211)
at org.springframework.batch.core.step.item.BatchRetryTemplate.execute(BatchRetryTemplate.java:217)
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.transform(FaultTolerantChunkProcessor.java:292)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:202)
at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:75)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:272)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:81)
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375)
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145)
at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257)
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:200)
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)
at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:66)
at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67)
at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:169)
at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144)
at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:136)
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:308)
at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:141)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:134)
at package30.Example30.main(Example30.java:23)
Caused by: java.lang.NullPointerException
at package30.ExceptionSimulator.process(ExceptionSimulator.java:13)
at package30.ExceptionSimulator.process(ExceptionSimulator.java:1)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:126)
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor$1.doWithRetry(FaultTolerantChunkProcessor.java:227)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:287)
… 26 more

Apr 11, 2020 10:42:47 AM org.springframework.batch.core.launch.support.SimpleJobLauncher run
INFO: Job: [FlowJob: [name=importEmployees]] completed with the following parameters: [{date=1586581967518}] and the following status: [FAILED]

3 thoughts on “Excluding exceptions from skipping

  1. And so what? What is your conclusion anout that?
    I’m facing the same issue, and I’d like to deactivate Retry simply

Leave a Reply