Skippable Exception - spring-batch

I am trying to skip all the exceptions during the batch run using the following config:
<chunk reader="aaaFileReader" writer="aaaDBWriter"
commit-interval="100" skip-limit="100000">
<skippable-exception-classes>
<include class="java.lang.Exception" />
<exclude
class="org.springframework.jdbc.CannotGetJdbcConnectionException" />
</skippable-exception-classes>
</chunk>
<listeners>
<listener ref="aaabatchFailureListener" />
</listeners>
And I handle the exception in my listener. But when Spring Batch actually encounters an exception its not being skipped and the batch run ends with a failed state. The actual exception is a FlatFileParse Exception. How do I skip the FlatFileParseException?
Here is the log :
:18:21.257 [main] DEBUG o.s.b.repeat.support.RepeatTemplate - Handling fatal exception explicitly (rethrowing first of 1): org.springframework.batch.core.step.skip.NonSkippableReadException: Non-skippable exception during read
15:18:21.257 [main] ERROR o.s.batch.core.step.AbstractStep - Encountered an error executing the step
org.springframework.batch.core.step.skip.NonSkippableReadException: Non-skippable exception during read
at org.springframework.batch.core.step.item.FaultTolerantChunkProvider.read(FaultTolerantChunkProvider.java:81) ~[spring-batch-core.jar:na]
at org.springframework.batch.core.step.item.SimpleChunkProvider$1.doInIteration(SimpleChunkProvider.java:106) ~[spring-batch-core.jar:na]
at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:367) ~[spring-batch-infrastructure.jar:na]
at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infr
Caused by: org.springframework.batch.item.file.FlatFileParseException: Parsing error at line: 5, input=[0254285458908060150983101150983 AK00055002035201401081044000804CK5861 00Twist,Oliver AT&T 20121208 ]
at org.springframework.batch.

You can add the FlatFileParseException class on your batch job config, for example:
<batch:chunk reader="customImportReader" writer="customImporter" processor="customProcessor" commit-interval="1" skip-limit="10">
<batch:skippable-exception-classes>
<batch:include class="org.springframework.batch.item.file.FlatFileParseException" />
</batch:skippable-exception-classes>
</batch:chunk>

As per Spring Batch Documentation some of the exceptions are not qualified as skippable.
In your case its clear from logs that org.springframework.batch.item.file.FlatFileParseException is not a skippable excepotion hence re throwing org.springframework.batch.core.step.skip.NonSkippableReadException.
Read more about Configuring Skip Logic section that says:
For any exception encountered, the skippability will be determined by the nearest superclass in the class hierarchy. Any unclassifed exception will be treated as 'fatal'.
Read more about NonSkippableReadException that says:
Fatal exception to be thrown when a read operation could not be skipped.

Create a custom fileReader and Override the doRead() method to always throw you CustomException.
public class CustomFlatFileItemReader extends FlatFileItemReader {
#Override
protected T doRead() throws Exception {
T itemRead=null;
try {
itemRead= super.doRead();
} catch (FlatFileParseException e) {
throw new MyException(e.getMessage(), e);
}
return itemRead;}
}
Override your job skip policy to always skip your custom exception as below:
.skipPolicy((Throwable T, int skipCount) -> {
if (T instanceof BatchServiceException)
return true;
else
return false;

Related

error spring batch

i'm working in spring batch
public void beforeStep(StepExecution stepExecution) {
ExecutionContext context = new ExecutionContext();
context.putString("resourceFileName", file.getFilePath());
System.out.println("display resource path : ");
System.out.println(context.get("resourceFileName"));
}
this line works successfully i got the right path
System.out.println(context.get("resourceFileName"));
in the xml item reader i tried to get this resource like this
<property name="resource" value="file:#{stepExecutionContext['resourceFileName']}" />
but i got this error :
Caused by: java.lang.IllegalArgumentException: The Resource must not be null.
znd sometimes this error
Input resource must exist (reader is in 'strict' mode)

Spring Batch: How retrieve the skipped exceptions for Testing purposes

I am working with Spring Batch 3.0.7.RELEASE version
About Skip I have the following:
<batch:chunk reader="personErrorSkipFileItemReader"
processor="personErrorSkipItemProcessor"
writer="personErrorSkipFileItemWriter"
commit-interval="100"
skip-limit="11">
<batch:skippable-exception-classes>
<batch:include class="org.springframework.batch.item.file.FlatFileParseException"/>
<batch:include class="com.manuel.jordan.batch.exception.PersonBatchException"/>
</batch:skippable-exception-classes>
</batch:chunk>
It works how is expected.
For logging and test purposes I need retrieve the following data:
skipped elements (for example 8 of 11) showing the amount such as:
By FlatFileParseException 5
By PersonBatchException 3
Even better if can show in what step and area (reader, processor, writer) was thrown each skipped item.
Working through JobExecution, I have the following
List<Throwable> exceptions = jobExecution.getAllFailureExceptions();
logger.info("exceptions size: {}", exceptions.size());
for(Throwable throwable : exceptions){
logger.error("Throwable Class: {}", throwable.getClass().getSimpleName());
logger.error("{}", throwable.getMessage());
}
List<Throwable> exceptions_ = jobExecution.getFailureExceptions();
logger.info("exceptions_ size: {}", exceptions_.size());
for(Throwable throwable : exceptions_){
logger.error("Throwable Class: {}", throwable.getClass().getSimpleName());
logger.error("{}", throwable.getMessage());
}
But the size of both collections are higher than 1 and show the exceptions just when the Job completes how FAILED.
Because 8 < 11, the Job completes how COMPLETED and the lists sizes returns 0.
implement SkipListener , It has three callback methods
1 :onSkipInProcess
2 :onSkipInWrite
3: onSkipInRead
http://docs.spring.io/spring-batch/apidocs/org/springframework/batch/core/SkipListener.html

Getting "java.lang.IllegalStateException: Pool is closed" exception during getTx() call

I am getting below exceptions in the logs, related to orientdb (2.2.19)
SYS_ERR: java.lang.IllegalStateException: Pool is closed
>
> at
> com.orientechnologies.orient.core.db.OPartitionedDatabasePool.checkForClose(OPartitionedDatabasePool.java:370)
>
> at
> com.orientechnologies.orient.core.db.OPartitionedDatabasePool.acquire(OPartitionedDatabasePool.java:176)
>
> at
> com.tinkerpop.blueprints.impls.orient.OrientBaseGraph.<init>(OrientBaseGraph.java:143)
>
> at
> com.tinkerpop.blueprints.impls.orient.OrientTransactionalGraph.<init>(OrientTransactionalGraph.java:77)
> at
> com.tinkerpop.blueprints.impls.orient.OrientGraph.<init>(OrientGraph.java:135)
> at
> com.tinkerpop.blueprints.impls.orient.OrientGraphFactory$1.getGraph(OrientGraphFactory.java:84)
>
> at
> com.tinkerpop.blueprints.impls.orient.OrientGraphFactory.getTx(OrientGraphFactory.java:221)
The code mainly perform adding the data in orientdb, and while getting the OrientGraphFactory.getTx() call, I am seeing all these exception.
I am calling commit() call, and then doing shutdown()
private void commitGraph(OrientBaseGraph graph){
try{
graph.commit();
}catch(Exception e){
e.printStackTrace();
}finally{
graph.shutdown();
}
}
Also these exception cased after NPE, while performing commit
java.lang.NullPointerException
at com.tinkerpop.blueprints.impls.orient.OrientBaseGraph.makeActive(OrientBaseGraph.java:362)
at com.tinkerpop.blueprints.impls.orient.OrientTransactionalGraph.commit(OrientTransactionalGraph.java:177)
The OrientGraphFactory is initialized as below
factory = new OrientGraphFactory(url, userName, password).setupPool(1,50);
Any pointers, when and why this error is thrown would be helpful.
Edit:
The configuration in my orientdb-server-config.xml is as below :
<parameters>
<parameter value="true" name="enabled"/>
<parameter value="50" name="graph.pool.max"/>
</parameters>
I could see that there are many exceptions for not reaching the remote server having orientdb installation. Is it something related to it. I could see the below exception
com.orientechnologies.orient.core.exception.OStorageException: Cannot create a connection to remote server address(es)

Stop task when there is an exception thrown in ItemProcessor

I am designing a Spring Batch, which reads multiple csv files. I have used partitioning to read each file in chunk and process it to decrypt a certain column in the csv. Before decrypting if i encounter any validation error , i throw custom exception.
Now what i want is if the processing finds any validation error in the first line, the other lines should not be processed, and the job should end.
How can i achieve this? I tried to implement ProcessorListener too but it has no StepExecution object so that i can call SetTerminateOnly() or ExitStatus=Failed
Also note that i have multiple thread accessing the file in different lines.I want to kill all threads in the event of the first encountered error.
Thanks in advance
So, I identified that running multiple asynchronous concurrent threads (Spring Batch partitioning) was the real issue. Though one of the thread threw an Exception, the other threads were parallely running, and finished executing till the end.
Ath the end, the Job FAILED overall and there was no output processed, but it consumed time to process rest of the data.
Well,the solution to it is as simple as it gets. We just need stop the Job while encountering an error during processing.
The Custom Processor
public class MultiThreadedFlatFileItemProcessor implements ItemProcessor<BinFileVO, BinFileVO>,JobExecutionListener{
private JobExecution jobExecution;
private RSADecrypter decrypter;
public RSADecrypter getDecrypter() {
return decrypter;
}
public void setDecrypter(RSADecrypter decrypter) {
this.decrypter = decrypter;
}
#Override
/**
This method is used process the encrypted data
#param item
* */
public BinFileVO process(BinFileVO item) throws JobException {
if(null!=item.getEncryptedText() && !item.getEncryptedText().isEmpty()){
String decrypted = decrypter.getDecryptedText(item.getEncryptedText());
if(null!=decrypted && !decrypted.isEmpty()){
if(decrypted.matches("[0-9]+")){
if(decrypted.length() >= 12 && decrypted.length() <= 19){
item.setEncryptedText(decrypted);
}else{
this.jobExecution.stop();
throw new JobException(PropertyLoader.getValue(ApplicationConstants.DECRYPTED_CARD_NO_LENGTH_INVALID),item.getLineNumber());
}
}
}else{
this.jobExecution.stop();
throw new JobException(PropertyLoader.getValue(ApplicationConstants.EMPTY_ENCRYPTED_DATA),item.getLineNumber());
}
return item;
}
#Override
public void beforeJob(JobExecution jobExecution) {
this.jobExecution=jobExecution;
}
#Override
public void afterJob(JobExecution jobExecution) {
}
}
The Job xml config
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
.....>
<!-- JobRepository and JobLauncher are configuration/setup classes -->
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" />
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<!-- Job Details -->
<job id="simpleMultiThreadsReaderJob" xmlns="http://www.springframework.org/schema/batch">
<step id="step" >
<partition step="step1" partitioner="partitioner">
<handler grid-size="5" task-executor="taskExecutor"/>
</partition>
</step>
<listeners>
<listener ref="decryptingItemProcessor"/>
</listeners>
</job>
<step id="step1" xmlns="http://www.springframework.org/schema/batch">
<tasklet>
<chunk reader="itemReader" writer="itemWriter" processor="decryptingItemProcessor" commit-interval="500"/>
<listeners>
<listener ref="customItemProcessorListener" />
</listeners>
</tasklet>
</step>
<!-- Processor Details -->
<bean id="decryptingItemProcessor" class="com.test.batch.io.MultiThreadedFlatFileItemProcessor">
<property name="decrypter" ref="rsaDecrypter" />
</bean>
<!-- RSA Decrypter class -->
<bean id="rsaDecrypter" class="test.batch.secure.rsa.client.RSADecrypter"/>
<!-- Partitioner Details -->
<bean class="org.springframework.batch.core.scope.StepScope" />
<bean id="partitioner" class="com.test.batch.partition.FlatFilePartitioner" scope="step">
<property name="resource" ref="inputFile"/>
</bean>
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10"/>
</bean>
<!-- Step will need a transaction manager -->
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
........
.................
</beans>
Here are the logs
2016-09-01 06:32:40 INFO SimpleJobRepository:273 - Parent JobExecution is stopped, so passing message on to StepExecution
2016-09-01 06:32:43 INFO ThreadStepInterruptionPolicy:60 - Step interrupted through StepExecution
2016-09-01 06:32:43 INFO AbstractStep:216 - Encountered interruption executing step: Job interrupted status detected.
; org.springframework.batch.core.JobInterruptedException
2016-09-01 06:32:45 ERROR CustomJobListener:163 - exception :At line No. 1 : The decrypted card number is less than 12 or greater than 19 in length
2016-09-01 06:32:45 ERROR CustomJobListener:163 - exception :Job interrupted status detected.
2016-09-01 06:32:45 INFO SimpleJobLauncher:135 - Job: [FlowJob: [name=simpleMultiThreadsReaderJob]] completed with the following parameters: [{outputFile=/usr/local/pos/bulktokenization/csv/outputs/cc_output_EDWError_08162016.csv, partitionFile=/usr/local/pos/bulktokenization/csv/partitions/, inputFile=C:\usr\local\pos\bulktokenization\csv\inputs\cc_input_EDWError_08162016.csv, fileName=cc_input_EDWError_08162016}] and the following status: [FAILED]
2016-09-01 06:32:45 INFO BatchLauncher:122 - Exit Status : FAILED
2016-09-01 06:32:45 INFO BatchLauncher:123 - Time Taken : 8969
If we throw Custom Exception in Processor, Spring Batch will terminate and mark the job failed unless you setup 'skipable' exception. You have not mentioned where you perform validate step, are you doing in Processor or Reader? Let me know because it is where Spring Batch decides.
In my project, if I want to stop the job and throw Custom Exception, we put validation logic in a Tasklet or Processor and throw exception as below
private AccountInfoEntity getAccountInfo(Long partnerId) {
if(partnerId != null){
.....
return ....;
} else {
throw new ReportsException("XXXXX");
}
}

FlowExecutionException when a caught exception occurs in ItemWriter implementing StepListener

I am having an exception caused in Spring Batch code that I suspect is due to some bad configuration. First I will give context and then the problem I am having.
I am using Spring Batch 2.2.6.RELEASE
I have a job defined like this (simplified excerpts that I consider are the relevant ones):
....
<batch:job id="job1">
<batch:step id="step1">
<batch:tasklet ref="myTasklet1"/>
</batch:step>
<batch:step id="step2" >
<batch:tasklet ref="myTasklet2"/>
</batch:step>
<batch:step id="step3">
<batch:tasklet>
<batch:chunk reader="myReader" processor="myProcessor" writer="myCompositeWriter" commit-interval="10" />
</batch:tasklet>
<batch:listeners>
<batch:listener ref="myWriter2" />
</batch:listeners>
</batch:step>
<batch:step id="step4" >
<batch:tasklet ref="myTasklet4"/>
</batch:step>
</batch:job>
...
<bean id="myCompositeWriter " class="org.springframework.batch.item.support.CompositeItemWriter">
<property name="delegates">
<list>
<ref bean="myWriter1" />
<ref bean="myWriter2" />
</list>
</property>
</bean>
<bean id="myWriter2" class="my.test.MyWriter2" scope="step" />
...
The simplified writer2 as follows:
public class MyWriter2 implements ItemWriter<Object>, StepExecutionListener {
private ExecutionContext jobContext;
#Override
public void beforeStep(StepExecution stepExecution) {
JobExecution jobExecution = stepExecution.getJobExecution();
this.jobContext = jobExecution.getExecutionContext();
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
return null;
}
#Override
public void write(List<? extends Object> items) {
try {
// database insertion
} catch (Exception e) {
// add exception to context for later notifications
jobContext.put("writer2_error", e);
}
}
}
Some requirements:
Need to access the jobContext from all tasklets and from writer2. Accessing the jobcontext from tasklets is straightforward. The writer2 implements StepExecutionListener and it is registered as a listener in step3 to be able to access it.
The writer2 inserts data into a database. This operation may fail but should allow the job to continue the execution and if everything else works fine then the job should end successfully. That is the reason why the insertion exceptions are all caught.
The problem:
If the writer operation in writer2 fails the exception is caught but the job fails after step3.
In Spring Batch Admin console the steps 1, 2 and 3 statuses are COMPLETED, the step 4 status is NONE, the job status and exit code is FAILED and the next exception is shown:
org.springframework.batch.core.JobExecutionException: Flow execution ended unexpectedly at
org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:141) at
org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:301) at
org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:134) at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745) Caused by:
org.springframework.batch.core.job.flow.FlowExecutionException: Ended flow=job1 at state=step3 with exception at
org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:160) at
org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:130) at
org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:135)
... 5 more Caused by: java.util.EmptyStackException at
org.codehaus.jettison.util.FastStack.peek(FastStack.java:39) at
org.codehaus.jettison.mapped.MappedXMLStreamWriter.setNewValue(MappedXMLStreamWriter.java:121) at
org.codehaus.jettison.mapped.MappedXMLStreamWriter.makeCurrentJSONObject(MappedXMLStreamWriter.java:113) at
org.codehaus.jettison.mapped.MappedXMLStreamWriter.writeStartElement(MappedXMLStreamWriter.java:241) at
com.thoughtworks.xstream.io.xml.StaxWriter.startNode(StaxWriter.java:162) at
com.thoughtworks.xstream.io.xml.AbstractXmlWriter.startNode(AbstractXmlWriter.java:37) at
com.thoughtworks.xstream.io.WriterWrapper.startNode(WriterWrapper.java:33) at
com.thoughtworks.xstream.io.path.PathTrackingWriter.startNode(PathTrackingWriter.java:44) at
com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper.startNode(ExtendedHierarchicalStreamWriterHelper.java:17) at
com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.writeItem(AbstractCollectionConverter.java:62) at
com.thoughtworks.xstream.converters.collections.MapConverter.marshal(MapConverter.java:57) at
com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:65) at
com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:78) at
com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:63) at com.thoughtworks.xstream.core.Tree
If no exceptions occur then the job ends successfully.
Any ideas why this could be failing?
Thanks.