Not able to access stepExecutionContext value in writer - spring-batch

I am setting the stepExecutionContext value in my partitioner and trying to get it in Writer.
But i could not able to access it.
The writer is step scoped.
Could any one help me how to get the step execution context values in writer?
Thanks
Shankar

you can implement StepExecutionListener in writer to get stepExecution in writer
public class ExampleWriter implements ItemWriter<T>,StepExecutionListener {
private JobExecution jobExecution;
#Override
public void write(List<? extends T> items) {
String executionContextValue=jobExecution.getExecutionContext().get("KEY");
System.out.println("ExecutionContextValue is:"+executionContextValue);
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
return null;
}
#Override
public void beforeStep(StepExecution stepExecution) {
this.jobExecution= stepExecution.getJobExecution();
}
}
If needed register this class as a listener in xml configuration
<listener>ExampleWriter</listener>

Related

Spring Batch Using CompositeItemWriter and CompositeItemProcessor

Using Spring Batch, I have to write in two different table, but using the same ItemReader.
I can't figure out how to use one ItemReader and a CompositeItemWriter.
Here's the JobConfiguration :
public class JobConfiguration {
#Autowired
#SuppressWarnings("squid:S3305")
private ItemReaderSurveillance itemReaderSurveillance;
#Autowired
#SuppressWarnings("squid:S3305")
private ItemWriterSurveillance itemWriterSurveillance;
#Autowired
#SuppressWarnings("squid:S3305")
private ItemWriterSurveillanceEcheance itemWriterSurveillanceEcheance;
#Autowired
#SuppressWarnings("squid:S3305")
private CompositeItemProcessorSurveillance compositeItemProcessor;
#Bean(name = "importSurveillanceJob")
public Job job(JobBuilderFactory jobs) {
return jobs.get("importSurveillanceStep")
.listener(jobListener)
.start(stepTaskletCreationRepertoireReport())
.next(stepTaskletCreationRepertoireArchive())
.next(stepSurveillanceReadProcessWrite())
.next(stepZipFile())
.build();
}
#Bean
protected Step stepSurveillanceReadProcessWrite() {
return stepBuilderFactory.get("stepSurveillanceReadProcessWrite")
.<SurveillanceLineFile, CompositeResultSurveillance>chunk(Integer.valueOf(commitInterval))
.reader(itemReaderSurveillance)
.processor(compositeItemProcessor)
.writer(compositeItemWriter())
.faultTolerant()
.retryLimit(0)
.build();
}
#Bean
public CompositeItemWriter<CompositeResultSurveillance> compositeItemWriter(){
CompositeItemWriter compositeItemWriter = new CompositeItemWriter();
compositeItemWriter.setDelegates(Arrays.asList(itemWriterSurveillance, itemWriterSurveillanceEcheance));
return compositeItemWriter;
}
}
Item Writers :
#Slf4j
#StepScope
#Component
public class ItemWriterSurveillance implements ItemWriter<FoaSurveillance>, StepExecutionListener {
String fileName;
JobExecution mJobExecution;
StepExecution stepExecution;
#Autowired
private FoaSurveillanceDao foaSurveillanceDao;
#Override
public void write(List<? extends FoaSurveillance> foaSurveillances) {
ExecutionContext executionContext = stepExecution.getExecutionContext();
// Process data
}
#Override
public void beforeStep(StepExecution stepExecution) {
mJobExecution = stepExecution.getJobExecution();
this.stepExecution = stepExecution;
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
return ExitStatus.COMPLETED;
}
}
#Slf4j
#StepScope
#Component
public class ItemWriterSurveillanceEcheance implements ItemWriter<FoaSurveillanceEcheance>, StepExecutionListener {
String fileName;
JobExecution mJobExecution;
StepExecution stepExecution;
#Autowired
private FoaSurveillanceEcheanceDao foaSurveillanceEcheanceDao;
#Override
public void write(List<? extends FoaSurveillanceEcheance> foaSurveillanceEcheances) {
ExecutionContext executionContext = stepExecution.getExecutionContext();
// Process data
}
#Override
public void beforeStep(StepExecution stepExecution) {
mJobExecution = stepExecution.getJobExecution();
this.stepExecution = stepExecution;
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
return ExitStatus.COMPLETED;
}
}
Compose the two itemProcessor :
#Slf4j
#Component
public class CompositeItemProcessorSurveillance implements ItemProcessor<SurveillanceLineFile, CompositeResultSurveillance>, StepExecutionListener {
private StepExecution stepExecution;
#Autowired
ItemProcessorSurveillance itemProcessorSurveillance;
#Autowired
ItemProcessorSurveillanceEcheance itemProcessorSurveillanceEcheance;
#Override
public CompositeResultSurveillance process(SurveillanceLineFile surveillanceLineFile) throws Exception {
CompositeResultSurveillance compositeResultSurveillance = new CompositeResultSurveillance();
compositeResultSurveillance.setFoaSurveillance(itemProcessorSurveillance.process(surveillanceLineFile));
compositeResultSurveillance.setFoaSurveillanceEcheance(itemProcessorSurveillanceEcheance.process(surveillanceLineFile));
return compositeResultSurveillance;
}
#Override
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
return null;
}
}
The ItemProcessorSurveillance :
#Slf4j
#Component
public class ItemProcessorSurveillance implements ItemProcessor<SurveillanceLineFile, FoaSurveillance>, StepExecutionListener {
String fileName;
private StepExecution stepExecution;
#Override
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
return ExitStatus.COMPLETED;
}
#Override
public FoaSurveillance process(SurveillanceLineFile surveillanceLineFile) throws Exception {
ExecutionContext executionContext = stepExecution.getExecutionContext();
// Process Data
}
And the CompositeResult returned by the Processor :
#Getter
#Setter
public class CompositeResultSurveillance {
private FoaSurveillance foaSurveillance;
private FoaSurveillanceEcheance foaSurveillanceEcheance;
}
For now I've got a NPE on the ItemProcessorSurveillance because stepExecution is null on the process method.
I can't figure out what's wrong. Any help ?
This is because your ItemProcessorSurveillance implements two interfaces: ItemProcessor and StepExecutionListener but is only registered as an ItemProcessor in the step. It should also be registered as a listener so that beforeStep is called when appropriate to set the stepExecution field.

store filenames in Spring Batch for send email

I’m writing an application in Spring Batch to do this:
Read the content of a folder, file by file.
Rename the files and move them to several folders.
Send two emails: one with successed name files processed and one with name files that throwed errors.
I’ve already get 1. and 2. but I need to make the 3 point. ¿How can I store the file names that have send to the writer method in an elegant way with Spring Batch?
You can use a Execution Context to store the values of file names which gets processed and also which fails with errors.
We shall have a List/ similar datastructure which has the file names after the business logic. Below is a small snippet for reference which implements StepExecutionListener,
public class FileProcessor implements ItemWriter<TestData>, StepExecutionListener {
private List<String> success = new ArrayList<>();
private List<String> failed = new ArrayList<>();
#Override
public void beforeStep(StepExecution stepExecution) {
}
#Override
public void write(List<? extends BatchTenantBackupData> items) throws Exception {
// Business logic which adds the success and failure file names to the list
after processing
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
stepExecution.getJobExecution().getExecutionContext()
.put("fileProcessedSuccessfully", success);
stepExecution.getJobExecution().getExecutionContext()
.put("fileProcessedFailure", failed);
return ExitStatus.COMPLETED;
}
}
Now we have stored the file names in the execution context which we will be able to use it in send email step.
public class sendReport implements Tasklet, StepExecutionListener {
private List<String> success = new ArrayList<>();
private List<String> failed = new ArrayList<>();
#Override
public void beforeStep(StepExecution stepExecution) {
try {
// Fetch the list of file names which we have stored in the context from previous step
success = (List<String>) stepExecution.getJobExecution().getExecutionContext()
.get("fileProcessedSuccessfully");
failed = (List<BatchJobReportContent>) stepExecution.getJobExecution()
.getExecutionContext().get("fileProcessedFailure");
} catch (Exception e) {
}
}
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
// Business logic to send email with the file names
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
logger.debug("Email Trigger step completed successfully!");
return ExitStatus.COMPLETED;
}
}

How do I access Job parameters in a Spring Batch Listener?

I'm working with ItemListenerSupport to do some error handling for ItemReadListener, ItemProcessListener, and ItemWriteListener. I want to access the job parameters in this instance. How do I fetch those? I tried #BeforeStep to inject the StepExecution and Jobexecution but neither worked.
To get the handle of Job Parameters you can implement StepExecutionListener to your listener Class to make use of Overridden methods beforeStep and afterStep
#Override
public void beforeStep(StepExecution stepExecution) {
String name = (String) stepExecution.getJobExecution().getExecutionContext()
.get("name");
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
if (stepExecution.getStatus() == BatchStatus.COMPLETED) {
return ExitStatus.COMPLETED;
}
return ExitStatus.FAILED;
}
You can declare your listener as a step scoped bean and inject job parameters in it, something like:
#Bean
#StepScope
public ItemReadListener itemReadListener(final #Value("#{jobParameters['name']}") String name) {
return new ItemListenerSupport() {
#Override
public void afterRead(Object item) {
System.out.println("in listener, job param name=" + name);
super.afterRead(item);
}
};
}

Spring Batch How to get JobExecution Object in Process Listner

I have requirement in my project what ever exception occur in ItemProccesor need to store Exception in JobExecution context and at the end of JobExecution send mail for Exceptional records but how to get JobExecution Object in processListner?
I tried using #beforestep in processListner but JobExecution object was null is there any way to get JobExecution context in process Listner
I got solution in spring batch for above issue, need to specify jobscope in process listener and access job execution context in listner class code is mention below.
#Bean
#JobScope
public CaliberatedProcessorListener calibratedProcessorListener() {
return new CaliberatedProcessorListener();
}
public class CaliberatedProcessorListener <T, S> implements ItemProcessListener<T, S> {
#Value("#{jobExecution}")
public JobExecution jobExecution;
#Override
public void beforeProcess(T calibratedProessorInPut) {
// // do nothing
}
#Override
public void afterProcess(T calibratedProessorInput, S calibratedProessorOutPut) {
// do nothing
}
#Override
public void onProcessError(T item, Exception calibratedProcessorEx) {
FtpEmailData ftpEmailData = (FtpEmailData) jobExecution.getExecutionContext().get("calDeviceBatchInfo");
ftpEmailData.getExceptionList().add(new CalibratedDeviceException(calibratedProcessorEx.getMessage()));
}
}

Issue retrieving a ExecutionContext from a SkipListener

I am trying to retrieve a spring batch ExecutionContext from a SkipListener.
Here is what I have attempted (I rely on annotations instead of interfaces in order to implement my listeners):
import com.xxxx.domain.UserAccount;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.annotation.BeforeStep;
import org.springframework.batch.core.annotation.OnSkipInWrite;
import org.springframework.mail.MailSendException;
import org.springframework.stereotype.Component;
#Slf4j
#Component
public class MailSkipListener {
private StepExecution stepExecution;
#BeforeStep
public void saveStepExecution(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
#OnSkipInWrite
public void logSkippedEmail(UserAccount userAccount, Throwable t) {
if (t instanceof MailSendException) {
MailSendException e = (MailSendException) t;
log.warn("FailedMessages: " + e.getFailedMessages());
}
}
}
However, the logSkippedEmail method is never executed when a MailSendException is raised. When I remove the saveStepExecution method, the logSkippedEmail is again executed in case of a MailSendException.
I register my MailSkipListener as follows:
#Bean
public Step messagesDigestMailingStep(EntityManagerFactory entityManagerFactory) {
return stepBuilderFactory
.get("messagesDigestMailingStep")
.<UserAccount, UserAccount>chunk(5)
...
.writer(itemWriter)
.listener(mailSkipListener)//Here
.build();
}
What I am trying to achieve here is retrieving an ExecutionContext from my SkipListener. How can this be achieved? It seems there's no way to autowire theExecutionContext.
This is quite an old question, but I just struggled with this too.
I ended up registering the skiplistener twice in order for it to work, once as a StepExecutionListener and another as a SkipListener.
It sucks, but it seems to work:
#Bean
public Step messagesDigestMailingStep(EntityManagerFactory entityManagerFactory) {
return stepBuilderFactory
.get("messagesDigestMailingStep")
.<UserAccount, UserAccount>chunk(5)
...
.writer(itemWriter)
.listener((StepExecutionListener) mailSkipListener) // <--- 1
.listener((SkipListener) mailSkipListener) // <--- 2
.build();
}
I know this is an old Question, but I had to deal with this myself, and have put together the following implementation, wherein I made the SkipListener also implement the StepExecutionListener, and adding the same class as both the SkipListener and StepExecutionListener.
#Component
public class PersonImportListener implements SkipListener<Person, Person>, StepExecutionListener {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private StepExecution stepExecution;
#Override
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
return ExitStatus.COMPLETED;
}
#Override
public void onSkipInRead(Throwable throwable) {
logger.warn("Line skipped on read", throwable);
}
#Override
public void onSkipInWrite(Person person, Throwable throwable) {
logger.warn("Bean skipped on write", throwable);
logger.warn("Execution Context" + stepExecution);
}
#Override
public void onSkipInProcess(Person person, Throwable throwable) {
logger.warn("Bean skipped on process", throwable);
}
}
And use this class as a listener for StepExecutionListener and also SkipListener.
#Bean
public Step step1(JdbcBatchItemWriter<Person> writer) {
PersonImportListener listener = new PersonImportListener();
return stepBuilderFactory.get("step1")
.<Person, Person> chunk(10)
.reader(reader())
.faultTolerant()
.skipLimit(10)
.skip(DataIntegrityViolationException.class)
.listener((StepExecutionListener) listener)
.listener((SkipListener) listener)
.processor(processor())
.writer(writer)
.build();
}
You can implement StepExecutionListener on your MailSkipListener to save the context in your stepExecution during the beforeStep() method :
public class MailSkipListener implements StepExecutionListener {
#Override
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}