4
votes

I would like to get data in the Writer that I've set in the Reader of my step. I know about ExecutionContexts (step and job) and about ExecutionContextPromotionListener via http://docs.spring.io/spring-batch/trunk/reference/html/patterns.html#passingDataToFutureSteps

The problem is that in Writer I'm retrieving a null value of 'npag'.

Line on ItemWriter:

LOG.info("INSIDE WRITE, NPAG: " + nPag);

I've being doing some workarounds without luck, looking answer for other similar questions... Any help? thanks!

Here's my code:

READER

@Component
public class LCItemReader implements ItemReader<String> {

private StepExecution stepExecution;

private int nPag = 1;

@Override
public String read() throws CustomItemReaderException {

    ExecutionContext stepContext = this.stepExecution.getExecutionContext();
    stepContext.put("npag", nPag);
    nPag++;
    return "content";
}

@BeforeStep
public void saveStepExecution(StepExecution stepExecution) {
    this.stepExecution = stepExecution;
}
}

WRITER

@Component
@StepScope
public class LCItemWriter implements ItemWriter<String> {

private String nPag;

@Override
public void write(List<? extends String> continguts) throws Exception {
    try {
        LOG.info("INSIDE WRITE, NPAG: " + nPag);
    } catch (Throwable ex) {
        LOG.error("Error: " + ex.getMessage());
    }
}

@BeforeStep
public void retrieveInterstepData(StepExecution stepExecution) {
    JobExecution jobExecution = stepExecution.getJobExecution();
    ExecutionContext jobContext = jobExecution.getExecutionContext();
    this.nPag = jobContext.get("npag").toString();
}
}

JOB/STEP BATCH CONFIG

@Bean
public Job lCJob() {
    return jobs.get("lCJob")
            .listener(jobListener)
            .start(lCStep())
            .build();
}

@Bean
public Step lCStep() {
    return steps.get("lCStep")
            .<String, String>chunk(1)
            .reader(lCItemReader)
            .processor(lCProcessor)
            .writer(lCItemWriter)
            .listener(promotionListener())
            .build();
}

LISTENER

@Bean
public ExecutionContextPromotionListener promotionListener() {
    ExecutionContextPromotionListener executionContextPromotionListener = new ExecutionContextPromotionListener();
    executionContextPromotionListener.setKeys(new String[]{"npag"});
    return executionContextPromotionListener;
}
3
Inject step execution into writer and read nPag during write()Luca Basso Ricci

3 Answers

6
votes

The ExecutionContextPromotionListener specifically states that it works at the end of a step so that would be after the writer executes. So the promotion I think you are counting on does not occur when you think it does.

If i were you I would set it in the step context and get it from the step if you need the value with in a single step. Otherwise I would set it to the job context.

The other aspect is the @BeforeStep. That marks a method for executing before the step context exists. The way you are setting the nPag value in the reader would be after the step had started executing.

3
votes

You are trying to read the value for nPag even before it is set in the reader, ending up with a default value which is null. You need to read the value on nPag at the time of logging from the execution context directly. You can keep a reference to the jobContext. Try this

@Component
@StepScope
public class LCItemWriter implements ItemWriter<String> {

private String nPag;
private ExecutionContext jobContext;

@Override
public void write(List<? extends String> continguts) throws Exception {
    try {
        this.nPag = jobContext.get("npag").toString();
        LOG.info("INSIDE WRITE, NPAG: " + nPag);
    } catch (Throwable ex) {
        LOG.error("Error: " + ex.getMessage());
    }
}

@BeforeStep
public void retrieveInterstepData(StepExecution stepExecution) {
    JobExecution jobExecution = stepExecution.getJobExecution();
    jobContext = jobExecution.getExecutionContext();

}
}
1
votes

In your Reader and Writer you need to implement ItemStream interface and use ExecutionContext as member variable.Here i have given example with Processor instead of Writer but same is applicable for Writer as well .Its working fine for me and i am able to take values from reader to processor.

I have set the value in context in reader and getting the value in processor.

public class EmployeeItemReader implements ItemReader<Employee>, ItemStream {

    ExecutionContext context;
    @Override
    public Employee read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
        context.put("ajay", "i am going well");
        Employee emp=new Employee();
        emp.setEmpId(1);
        emp.setFirstName("ajay");
        emp.setLastName("goswami");
        return emp;
    }
    @Override
    public void close() throws ItemStreamException {
        // TODO Auto-generated method stub
    }

    @Override
    public void open(ExecutionContext arg0) throws ItemStreamException {
        context = arg0;
    }

    @Override
    public void update(ExecutionContext arg0) throws ItemStreamException {
        // TODO Auto-generated method stub
        context = arg0;
    }

}

My processor

public class CustomItemProcessor implements ItemProcessor<Employee,ActiveEmployee>,ItemStream{
    ExecutionContext context;
    @Override
    public ActiveEmployee process(Employee emp) throws Exception {
        //See this line
        System.out.println(context.get("ajay"));

        ActiveEmployee actEmp=new ActiveEmployee();
        actEmp.setEmpId(emp.getEmpId());
        actEmp.setFirstName(emp.getFirstName());
        actEmp.setLastName(emp.getLastName());
        actEmp.setAdditionalInfo("Employee is processed");
        return actEmp;
    }
    @Override
    public void close() throws ItemStreamException {
        // TODO Auto-generated method stub

    }
    @Override
    public void open(ExecutionContext arg0) throws ItemStreamException {
        // TODO Auto-generated method stub

    }
    @Override
    public void update(ExecutionContext arg0) throws ItemStreamException {
        context = arg0;

    }

}

Hope this helps.