Configuring Multiple SkipListeners

In this post under Spring Batch, I will show with example how to configure multiple Skip listeners.

In my previous post, I showed how to configure a bean as skip listeners. For recap purpose below is the xml for your reference

1 	<bean id="mySkipListener" class="package25.MySkipListener"/>
2 	
3 	<batch:job id="importEmployees">
4 		<batch:step id="readWriteEmployees">
5 			<batch:tasklet>
6 				<batch:chunk reader="reader" writer="writer" commit-interval="50" skip-limit="200">
7 					<batch:skippable-exception-classes>
8 						<batch:include class="org.springframework.batch.item.file.FlatFileParseException"/>
9 					</batch:skippable-exception-classes>
10				</batch:chunk>
11				<batch:listeners>
12					<batch:listener ref="mySkipListener"/>
13				</batch:listeners>
14			</batch:tasklet>
15		</batch:step>
16	</batch:job>

In the above xml code, at line 1 we have defined a bean for class “MySkipListener” named “mySkipListener”.

We refer that bean at line 12 using “listener” batch xml element.

In this way we register a skip listener.

Now I will show how to register multiple skip listeners.

For our example we create two skip listener classes “MySkipListener1” and “MySkipListener2”. The code is as shown below

MySkipListener1

package xml.package26;

import org.springframework.batch.core.SkipListener;
import org.springframework.batch.item.file.FlatFileParseException;

public class MySkipListener1 implements SkipListener<Employee, Employee> {
	@Override
	public void onSkipInProcess(Employee employee, Throwable throwable) {
	}

	@Override
	public void onSkipInRead(Throwable throwable) {
		if(throwable instanceof FlatFileParseException) {
			FlatFileParseException flatFileParseException = (FlatFileParseException) throwable;
			String message = flatFileParseException.getMessage() + "-" + flatFileParseException.getLineNumber();
			System.out.println("MySkipListener1" + message);
		}
	}

	@Override
	public void onSkipInWrite(Employee employee, Throwable throwable) {
	}
}

MySkipListener1

package xml.package26;

import org.springframework.batch.core.SkipListener;
import org.springframework.batch.item.file.FlatFileParseException;

public class MySkipListener2 implements SkipListener<Employee, Employee> {
	@Override
	public void onSkipInProcess(Employee employee, Throwable throwable) {
	}

	@Override
	public void onSkipInRead(Throwable throwable) {
		if(throwable instanceof FlatFileParseException) {
			FlatFileParseException flatFileParseException = (FlatFileParseException) throwable;
			String message = flatFileParseException.getMessage() + "-" + flatFileParseException.getLineNumber();
			System.out.println("MySkipListener2" + message);
		}
	}

	@Override
	public void onSkipInWrite(Employee employee, Throwable throwable) {
	}
}

Now below is the xml code to register both the classes as skip listeners

1 	<bean id="mySkipListener1" class="xml.package26.MySkipListener1"/>
2 	<bean id="mySkipListener2" class="xml.package26.MySkipListener2"/>
3 	
4 	<batch:job id="importEmployees">
5 		<batch:step id="readWriteEmployees">
6 			<batch:tasklet>
7 				<batch:chunk reader="reader" writer="writer" commit-interval="50" skip-limit="200">
8 					<batch:skippable-exception-classes>
9 						<batch:include class="org.springframework.batch.item.file.FlatFileParseException"/>
10					</batch:skippable-exception-classes>
11				</batch:chunk>
12				<batch:listeners>
13					<batch:listener ref="mySkipListener2"/>
14					<batch:listener ref="mySkipListener1"/>
15				</batch:listeners>
16			</batch:tasklet>
17		</batch:step>
18	</batch:job>

In the above xml code at line 1 and line 2, we have created two beans definitions, one for “MySkipListener1” class named “mySkipListener1” and another for
“MySkipListener2” class named “mySkipListener2”.

Next at line 13 and 14, I have registered those listeners using “listener” batch xml element.

The listeners will be called in the order they are listed in the xml code.

So in this example, mySkipListener2 class’s method will be called and then mySkipListener1 class’s method.

Below is the complete xml code 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"
	   xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	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
						http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">
	
	<bean id="employee" class="xml.package26.Employee" scope="prototype"/>
	
	<bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
		<property name="resource" value="file:EmployeeRecordsWithFileParseException.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="mySkipListener1" class="xml.package26.MySkipListener1"/>
	<bean id="mySkipListener2" class="xml.package26.MySkipListener2"/>
	
	<batch:job id="importEmployees">
		<batch:step id="readWriteEmployees">
			<batch:tasklet>
				<batch:chunk reader="reader" writer="writer" commit-interval="50" skip-limit="200">
					<batch:skippable-exception-classes>
						<batch:include class="org.springframework.batch.item.file.FlatFileParseException"/>
					</batch:skippable-exception-classes>
				</batch:chunk>
				<batch:listeners>
					<batch:listener ref="mySkipListener2"/>
					<batch:listener ref="mySkipListener1"/>
				</batch:listeners>
			</batch:tasklet>
		</batch:step>
	</batch:job>

	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>

	<bean id="dataSource"
		  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="org.h2.Driver" />
		<property name="url" value="jdbc:h2:file:~/repository" />
		<property name="username" value="" />
		<property name="password" value="" />
	</bean>

	<!-- create job-meta tables automatically -->
	<jdbc:initialize-database data-source="dataSource">
		<jdbc:script
				location="org/springframework/batch/core/schema-drop-h2.sql" />
		<jdbc:script location="org/springframework/batch/core/schema-h2.sql" />
	</jdbc:initialize-database>

	<bean id="jobRepository"
		  class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="transactionManager" ref="transactionManager"/>
		<property name="databaseType" value="h2"/>
	</bean>

	<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.TaskExecutorJobLauncher">
		<property name="jobRepository" ref="jobRepository"/>
	</bean>
</beans>

Leave a comment