RecordSeparatorPolicy interface Example

In this post of Spring Batch, I will explain with an example, the purpose of RecordSeparatorPolicy interface and how to use it.

In all my previous posts under Spring Batch, the examples (i.e., input file to read from) that I used, had a collection of records, where each record were delimited by new line as shown below.

Example 1

EMP_ID0,EMP_NAME0,EMP_STATUS0,0
EMP_ID1,EMP_NAME1,EMP_STATUS1,1
EMP_ID2,EMP_NAME2,EMP_STATUS2,2
EMP_ID3,EMP_NAME3,EMP_STATUS3,3

The new line indicated the end of one record and start of another new record.

Not all input files will be in this manner. Below is example where new line cannot be used to figure out the end of record.

Example 2

EMP_ID0,EMP_FNAME0 EMP_LNAME0,
EMP_STATUS0,0
EMP_ID1,EMP_FNAME1 EMP_LNAME1,
EMP_STATUS1,1
EMP_ID2,EMP_FNAME2 EMP_LNAME2,
EMP_STATUS2,2
EMP_ID3,EMP_FNAME3 EMP_LNAME3,
EMP_STATUS3,3

If we analyze the example, we can conclude that if the count of fields is 4 then we can say it is record.

Similar to above example, they may be many situations, where we need to come up with a logic to tell the reader how to identify a record.

An implementation of RecordSeparatorPolicy interface can be used to place these kind of logics.

RecordSeparatorPolicy interface has methods which can be used to tell reader what forms a record.

The methods of RecordSeparatorPolicy interface are as shown below
1) boolean isEndOfRecord(java.lang.String record)
2) java.lang.String postProcess(java.lang.String record)
3) java.lang.String preProcess(java.lang.String record)

For our requirement, we need to focus on isEndOfRecord method. This method returns a boolean value, true means end of record is reached and false means end of record not yet reached.

In this method, we can place the logic for our example 2, as shown below

CustomRecordSeparatorPolicy


1  package package15;
2  
3  import org.springframework.batch.item.file.separator.RecordSeparatorPolicy;
4  
5  public class CustomRecordSeparatorPolicy implements RecordSeparatorPolicy {
6   @Override
7   public boolean isEndOfRecord(String record) {
8       int fieldCount = record.split(",").length;
9       if(fieldCount == 4) {
10          return true;
11      } else {
12          return false;
13      }
14  }
15 
16  @Override
17  public String postProcess(String record) {
18      return record;
19  }
20 
21  @Override
22  public String preProcess(String record) {
23      return record;
24  }
25 }

As shown in code of isEndOfRecord method, we return true if the fieldCount is 4 else false.

Now we have to integrate CustomRecordSeparatorPolicy to the reader instance as shown in below xml configuration


1   <bean id="customRecordSeparatorPolicy" class="package15.CustomRecordSeparatorPolicy"/>
2   
3   <bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
4       <property name="resource" value="file:FileInput2.txt"/>
5       <property name="recordSeparatorPolicy" ref="customRecordSeparatorPolicy"/>
6       <property name="lineMapper">
7           <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
8               <property name="lineTokenizer">
9                   <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
10                      <property name="names" value="id,name,status,salary"/>
11                  </bean>
12              </property>
13              <property name="fieldSetMapper">
14                  <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
15                      <property name="prototypeBeanName" value="employee"/>
16                  </bean>
17              </property>
18          </bean>
19      </property> 
20  </bean>

At line 1, we create a bean definition for CustomRecordSeparatorPolicy class.

At line 3, we are telling the reader bean to use customRecordSeparatorPolicy bean to determine the end of record, by setting recordSeparatorPolicy property of the reader bean.

In this way we can use RecordSeparatorPolicy interface.

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="package15.Employee" scope="prototype"/>
    
    <bean id="customRecordSeparatorPolicy" class="package15.CustomRecordSeparatorPolicy"/>
    
    <bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
        <property name="resource" value="file:FileInput2.txt"/>
        <property name="recordSeparatorPolicy" ref="customRecordSeparatorPolicy"/>
        <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="delimiter" value="|" />
                <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>
    
    <batch:job id="importProductsJob">
        <batch:step id="readWriteProducts">
            <batch:tasklet>
                <batch:chunk reader="reader" 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>
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s