Home > database >  How to do validation and catching error with Skiplistener in Spring batch?
How to do validation and catching error with Skiplistener in Spring batch?

Time:09-10

I am trying to validate some fields of User with Validator. I am not able to catch the error in ItemProcessListener.

My validator is like so.

public class UserValidator implements Validator<User> {

    @Override
    public void validate(User user) throws ValidationException {
        if (user.getName().length() > 3) {
            throw new ValidationException("User name cannot have more than 3 alphabets");
        }
    }
}

I have Skiplistener like so..

public class UserValidationListener implements SkipListener<User, User> {

    @Override
    public void onSkipInRead(Throwable throwable) {
        System.out.println(throwable.getMessage());
    }

    @Override
    public void onSkipInWrite(User user, Throwable throwable) {
        System.out.println(user.toString());
        System.out.println(throwable.getMessage());
    }

    @Override
    public void onSkipInProcess(User user, Throwable throwable) {
        System.out.println(user.toString());
        System.out.println(throwable.getMessage());
        // No error message comes here
        // 1. get the error message thrown by Validation exception
        // 2. Save it db logic

    }
}

In the batch job config I have created a bean to filter out the faulty user data.

   @Bean
    public ValidatingItemProcessor<User> validatingItemProcessor() {
        ValidatingItemProcessor<User> itemProcessor = new ValidatingItemProcessor<>(new UserValidator());
        itemProcessor.setFilter(true);
        return itemProcessor;
    }

And finally here is my batch job config.


        Step step = stepBuilderFactory.get("ETL-file-load")
                .<User, User>chunk(100)
                .reader(itemReader)
                .processor(validatingItemProcessor())
                .writer(itemWriter)
                .faultTolerant()
                .skip(ValidationException.class)
                .listener(userValidationListener())
                .build();

        return jobBuilderFactory.get("ETL-Load")
                .incrementer(new RunIdIncrementer())
                .start(step)
                .build();

I have created a bean for listener like so:

    @Bean
    public UserValidationListener userValidationListener() {
        return new UserValidationListener();
    }

I am not able to catch the error message thrown by the UserValidator with ValidationException. How do I do that?

CodePudding user response:

Here's an example that skips odd numbers and writes even numbers, hopefully this will help you

import org.springframework.batch.core.Job;
import org.springframework.batch.core.SkipListener;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.step.skip.SkipPolicy;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.item.validator.ValidatingItemProcessor;
import org.springframework.batch.item.validator.ValidationException;
import org.springframework.batch.item.validator.Validator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;

@Configuration
@EnableBatchProcessing
public class SimpleJobConfig {

    @Bean
    public Job simpleJob(JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory.get("SimpleJob")
                .start(simpleJobStep(null))
                .build();
    }

    @Bean
    public Step simpleJobStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory.get("simpleJobStep")
                .<Integer, Integer>chunk(1)
                .reader(integerReader())
                .processor(validatingItemProcessor())
                .writer(integerWriter())
                .faultTolerant()
                .skipPolicy(skipPolicy())
                .listener(skipListener())
                .build();
    }

    @Bean
    public ItemReader<Integer> integerReader() {
        return new ListItemReader<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
    }

    @Bean
    public ValidatingItemProcessor<Integer> validatingItemProcessor() {
        return new ValidatingItemProcessor<>(evenNumberValidator());
    }

    @Bean
    public ItemWriter<Integer> integerWriter() {
        return items -> items.forEach(item -> System.out.println("Writing "   item));
    }

    public Validator<Integer> evenNumberValidator() {
        return value -> {
            if (value % 2 != 0) {
                throw new ValidationException("Odd number invalid");
            }
        };
    }

    public SkipPolicy skipPolicy() {
        return (t, skipCount) -> t instanceof ValidationException;
    }

    public SkipListener<Integer, Integer> skipListener() {
        return new SkipListener<Integer, Integer>() {
            @Override
            public void onSkipInRead(Throwable t) {}

            @Override
            public void onSkipInWrite(Integer item, Throwable t) {}

            @Override
            public void onSkipInProcess(Integer item, Throwable t) {
                System.out.println("Skipped "   item   " - "   t.getMessage());
            }
        };
    }
}

Output from job is

Skipped 1 - Odd number invalid
Writing 2
Skipped 3 - Odd number invalid
Writing 4
Skipped 5 - Odd number invalid
Writing 6
Skipped 7 - Odd number invalid
Writing 8
Skipped 9 - Odd number invalid
Writing 10

CodePudding user response:

Items that fail validation will be skipped so you need to add a SkipListener and declare ValidationException as skippable in your step

  • Related