Validating Items using ValidatingItemProcessor

In this post under Spring Batch, I will introduce ValidatingItemProcessor and explain how to use it with an example.

ValidatingItemProcessor is a implementation of org.springframework.batch.item.ItemProcessor interface.

As mentioned in my previous posts, implementation of ItemProcessor is used to place business logic.

The implementation of ItemProcessor which contains the business logic is placed between the reader and writer. So the business logic is executed after an item is read
and before an item is written.

Validation is one of the business logic that can be placed in an ItemProcessor.

Spring Batch comes with default of implementation of ItemProcessor which does the validation and that is ValidatingItemProcessor class.

ValidatingItemProcessor delegates the validation to an implementation of org.springframework.batch.item.validator.Validator interface.

We add our business specific validation logic in an implementation of org.springframework.batch.item.validator.Validator interface.

ValidatingItemProcessor validates the item read and based on the way it is configured, it throws an exception or returns null (informing the writer not to write the item).

How the ValidatingItemProcessor should react if an item fails validation, is decided based on the value of boolean flag “filter”.

If “filter” flag is set to true, the processor returns null.
If “filter” flag is set to false, the processor throws exception.

Below is an implementation of org.springframework.batch.item.validator.Validator interface.

EmployeeValidator


package package22;

import java.math.BigDecimal;

import org.springframework.batch.item.validator.ValidationException;
import org.springframework.batch.item.validator.Validator;

public class EmployeeValidator implements Validator {
    @Override
    public void validate(Employee employee) throws ValidationException {
        if(employee.getSalary().compareTo(BigDecimal.ZERO) == -1) {
            throw new ValidationException("Salary is less than zero.");
        }
    }
}

The above class validates an instance of Employee class. If the employee’s salary is negative it throws ValidationException. If “filter” flag in the ValidatingItemProcessor is set to false, ValidatingItemProcessor rethrows the exception thrown by EmployeeValidator instance.

Below is the structure of Employee class

Employee


package package22;

import java.math.BigDecimal;

public class Employee {
    private String id;
    private String name;
    private String status;
    private BigDecimal salary;
    
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status;
    }
    public BigDecimal getSalary() {
        return salary;
    }
    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
}

Now I will show an xml snippet that shows how to integrate EmployeeValidator instance with ValidatingItemProcessor instance and then how to place ValidatingItemProcessor instance between the reader and writer.

XML code


1   <bean id="employeeValidator" class="package22.EmployeeValidator"/>
2   
3   <bean id="validatingProcessor" class="org.springframework.batch.item.validator.ValidatingItemProcessor">
4       <property name="filter" value="true"/>
5       <property name="validator" ref="employeeValidator"/>
6   </bean>
7   
8   <batch:job id="importEmployees">
9       <batch:step id="readWriteEmployees">
10          <batch:tasklet>
11              <batch:chunk reader="reader" processor="validatingProcessor" writer="writer" commit-interval="50"/>
12          </batch:tasklet>
13      </batch:step>
14  </batch:job>

At line 1 we create a bean by name “employeeValidator” of type EmployeeValidator.

At line 3 we create a bean by name “validatingProcessor” of type ValidatingItemProcessor. Note in this example we set the “filter” property to true.

At line 5 we set the “validator” property of “validatingProcessor” bean to the “employeeValidator” created at line 1.

Now at line 11 we place the bean “validatingProcessor” between the reader and writer by setting the “processor” property of “batch:chunk” element.

In this way we configure the ValidatingItemProcessor and Validator implementation in xml file.

Below is the complete xml file


<?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="package22.Employee" scope="prototype"/>
    
    <bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
        <property name="resource" value="file:FileInput7.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="employeeValidator" class="package22.EmployeeValidator"/>
    
    <bean id="validatingProcessor" class="org.springframework.batch.item.validator.ValidatingItemProcessor">
        <property name="filter" value="true"/>
        <property name="validator" ref="employeeValidator"/>
    </bean>
    
    <batch:job id="importEmployees">
        <batch:step id="readWriteEmployees">
            <batch:tasklet>
                <batch:chunk reader="reader" processor="validatingProcessor" writer="writer" commit-interval="50"/>
            </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>

Leave a Reply