Search code examples
spring-batchfactory-pattern

Use of Factory Pattern in Spring Batch FileHeaderFieldSetMapper


I am setting up a Spring Batch job which would process a multi-record file. One of the record is having the following structure.

Ind,RecCode,AccNum,RecTypeDesc,EffectiveDate,CreditAmount,DebitAmount,BatchNumber,NumberOfTrasactions 
5, 20, 100, prl, 12084, as0, 0, 3, 1

Depending upon the value in the RecordCode field, it will be either a payment record or a reversal record.

I am setting up the PatternMatchingCompositeLineMapper to read the record types in the file. I have created the following 2 FieldSetMapper implementations classes

@Component("paymentBatchFieldSetMapper")
public class  PaymentBatchFieldSetMapper implements FieldSetMapper<PaymentBatch> {

    @Override
    public PaymentBatch mapFieldSet(FieldSet fieldSet) throws BindException {
        // TODO Auto-generated method stub
        return null;
    }
}

and second one as,

@Component("reversalBatchFieldSetMapper")
public class  ReversalBatchFieldSetMapper implements FieldSetMapper<PaymentBatch> {

    @Override
    public PaymentBatch mapFieldSet(FieldSet fieldSet) throws BindException {
        // TODO Auto-generated method stub
        return null;
    }
}

I have created a factory class as following to return either of the above 2 as below

@Component("achBatchFieldSetMapperFactory")
public class ACHBatchFieldSetMapperFactory {

    @Autowired
    private ApplicationContext applicationContext;

    public FieldSetMapper<? extends AbstractACHBatch> getFieldSetMapper(RecordTypeDescription recordTypeDescription) {
        FieldSetMapper<? extends AbstractACHBatch> fieldSetMapper = null;
        switch(recordTypeDescription) {
        case PAYMENT:
            fieldSetMapper = applicationContext.getBean(PaymentBatchFieldSetMapper.class);
            break;
        case REVERSAL:
            fieldSetMapper = applicationContext.getBean(ReversalBatchFieldSetMapper.class);
            break;
        }
        return fieldSetMapper;
    }
}

Below is the bean configuration for the map object to be injected into PatternMatchingCompositeLineMapper.

@Bean
public Map<String, LineTokenizer> fieldSetMapperMap(FileHeaderFieldSetMapper fileHeaderFieldSetMapper, PaymentBatchFieldSetMapper paymentBatchFieldSetMapper,
        ReversalBatchFieldSetMapper reversalBatchFieldSetMapper) {
    Map<String, FieldSetMapper> map = new HashMap<String, FieldSetMapper>();
    map.put("1*", fileHeaderFieldSetMapper);
    map.put("5*", ?????);                   
}

Is there a possibility that I can use the factory to return the needed FieldSet mapper depending upon the record in the file?


Solution

  • The item reader is designed to return one type of items at a time. Let's imagine we manage to map records to either PaymentBatch or ReversalBatch according to the RecordCode field, this means that after the mapping occurs, the ItemReader would return two types. This is not possible by design.

    What you can do is create a POJO that has a one-to-one mapping with the fields in your input file, then use a ClassifierCompositeItemProcessor to process each item according to its type (The classifier would be based on the RecordTypeDescription enum in your case).

    NB: It is of course technically possible to forget about the generic type of the ItemReader<T> interface and make the item reader return Objects, but this would require ugly usage of instanceof in your processor to determine the record type. I highly recommend not using this approach.