Overview
How to handle exception generated at the time of job execution is explained.
Since this function has different usage for chunk model and tasklet model, each will be explained.
First, classification of exceptions is explained, and handling method according to the type of exception is explained.
Classification of exception
The exception generated at the time of job execution are classified into 3 types as below.
Sr.No. |
Classification |
Description |
|
(1) |
Exception wherein the cause can be resolved by re-execution of the job (change / modification of parameter, input data etc). |
For the exception wherein the cause can be resolved by re-execution of a job, exception is handled in the application code and exception handling is performed. |
Business exception |
(2) |
Exception that cannot be resolved by job re-execution. |
Exceptions that can be resolved by job re-execution are handled with the following pattern. 1. If the exception can be captured in StepListener,
exception is handled in the application code. 2. If the exception cannot be captured in StepListener, exception is handled in the framework. |
|
(3) |
(During asynchronous execution)Exception caused by illegal request for job request |
Exception caused by illegal request of job request is handled in the framework and performs exception handling. In case of Asynchronous execution (DB polling) in the polling process, the validity of the job request is not verified.Therefore, it is desirable that the input check for the request is made in advance by the application that registers the job request. In case of Asynchronous execution (Web container), it is assumed that the input check for the request is made in advance, by the Web application. Therefore, exception handling is performed in an application that accepts requests or job requests. |
Avoid a transaction processing within the exception processing
If transactional processing such as writing to a database is performed in exception processing, a secondary exception is likely to occur. Exception processing should be based on output log analysis and end code setting. |
Exception type
Types of exceptions are explained.
Business exception
A business exception is an exception notifying that a violation of a business rule has been detected.
This exception is generated within the logic of the step.
Since it is assumed to be an application state, handling by system operator is not required.
-
When "out-of-stock" at the time of inventory allocation
-
When the number of days exceeds the scheduled date
-
etc …
Applicable exception class
|
Library exception occurring during normal operation
A library exception that occurs during normal operation refers to an exception that may occur when the system is operating normally, among the exceptions generated in the framework and library.
Exceptions raised in the framework and library are exception classes that occur in the Spring Framework and other libraries.
Since it is assumed to be an application state, it is not necessary to deal with the system operator.
-
Optimistic lock exception which occurs in exclusive control with online processing.
-
Unique constraint exception that occurs when registering the same data at the same time from multiple jobs or online processing.
-
etc …
Applicable exception class
|
System exception
A system exception is an exception to notify that a state that should not occur is detected when the system is operating normally.
This exception is generated within the logic of the step.
The action of the system operator is required.
-
When master data, directory, file, etc which should exist in advance, do not exist.
-
When an exception classified as system abnormality is captured (IOException at file operation, etc.) from the checked exceptions occurring in the framework or library.
-
etc…
Applicable exception class
|
Unexpected system exception
Unexpected system exceptions are non-inspection exceptions that do not occur when the system is operating normally.
It is necessary for the system operator to deal with it or to analyze it by the system developer.
Unexpected system exceptions will not be handled except by doing the following processing. If handled, throw the exception again.
-
Log capture exception for analysis and set the corresponding exit code.
-
Bugs are hidden in applications, frameworks, and libraries.
-
When the database server is down.
-
etc…
Applicable exception class
|
Fatal error
A fatal error is an error that notifies that a fatal problem has occurred that affects the entire system (application).
It is necessary for system operator or system developer to cope with it and recover.
Fatal errors are not handled except for the following processing.If handled, throw the exception again.
-
Log capture exception for analysis and set the corresponding exit code.
-
When memory available for Java virtual machine is insufficient.
-
etc…
Applicable exception class
|
Invalid job request error
The invalid job request error is an error to notify that a problem has occurred in the request for job request during asynchronous execution.
It is necessary for the system operator to cope with and recover from it.
The invalid job request error is an error based on exception handling in the application which processes job requests, and hence it is not explained in this guideline.
How to handle exceptions
How to handle exceptions is explained.
The exception handling pattern is as follows.
-
Decide whether to continue the job when an exception occurs (3 types)
-
Decide how to re-execute the suspended job (2 types)
Sr.No. | How to handle exceptions | Description |
---|---|---|
(1) |
Skip error record and continue processing. |
|
(2) |
Reprocess the error record until the specified condition (number of times, time etc.) is reached. |
|
(3) |
Processing is interrupted. |
Even if an exception has not occurred, the job may stop while processing because the job has exceeded the expected processing time. |
Sr.No. | How to handle exceptions | Description |
---|---|---|
(1) |
Re-executes the suspended job from the beginning. |
|
(2) |
Re-executes the interrupted job from the point where it was interrupted. |
For details, please refer how to re-execute the suspended job Rerun processing.
Skip
Skipping is a method of skipping error data without stopping batch processing and continuing processing.
-
Invalid record exists in input data
-
When a business exception occurs
-
etc …
Reprocess skipped record
When skipping the records, design how to deal with skipped invalid records. Methods like extracting and reprocessing invalid records, processing the records by including those at the time of subsequent execution can be considered. |
Retry
Retrying is a method of repeatedly attempting until a specified number of times or time is reached for a record that failed a specific process.
It is used only when the cause of processing failure depends on the execution environment and it is expected to be resolved over time.
-
When the record to be processed is locked by exclusive control
-
When message transmission fails due to instantaneous interruption of network
-
etc …
Application of retry
If the retry is applied in every scene, the processing time unnecessarily increases at the time of occurrence of an abnormality resulting in risk of delayed detection of the abnormality. |
Process interruption
Process interruption is literally a method of interrupting processing midway.
It is used when processing cannot be continued on detecting an erroneous content or when there is requirement which does not allow skipping of records.
-
Invalid record exists in input data
-
When a business exception occurs
-
etc …
How to use
Implementation of exception handling is explained.
A log is the main user interface for batch application operation.Therefore, monitoring of exception occurred will also be done through the log.
In Spring Batch, if an exception occurs during step execution, the log is output and process is abnormally terminated, so the requirement can be satisfied without additional implementation by the user. The following explanation should be implemented pinpoint only when it is necessary for the user to output logs according to the system. Basically, all the processes are not required to be implemented.
For common log setting of exception handling, please refer Logging.
Step unit exception handling
Explain how to handle exceptions in step units.
- Exception handling with ChunkListener interface
-
If you want to handle exceptions uniquely regardless of the processing model, use ChunkListener interface.
Although it can be implemented by using a step or job listener which is wider in scope than chunk, adoptChunkListener
and put an emphasis on carrying out the handling immediately after the occurrence.
The exception handling method for each processing model is as follows.
- Exception handling in chunk model
-
Implement the function using various Listener interfaces provided by Spring Batch.
- Exception handling in tasklet model
-
Implement exception handling independently within tasklet implementation.
Why unified handling possible with ChunkListener.
A sense of incompatibility might be felt with This point also appears in
|
Exception handling with ChunkListener interface
Implement afterChunkError
method of ChunkListener interface.
Get error information from ChunkContext
argument of afterChunkError
method using ChunkListener.ROLLBACK_EXCEPTION_KEY
as a key.
For details on how to set the listener,please refer Listerner setting.
@Component
public class ChunkAroundListener implements ChunkListener {
private static final Logger logger =
LoggerFactory.getLogger(ChunkAroundListener.class);
@Override
public void beforeChunk(ChunkContext context) {
logger.info("before chunk. [context:{}]", context);
}
@Override
public void afterChunk(ChunkContext context) {
logger.info("after chunk. [context:{}]", context);
}
// (1)
@Override
public void afterChunkError(ChunkContext context) {
logger.error("Exception occurred while chunk. [context:{}]", context,
context.getAttribute(ChunkListener.ROLLBACK_EXCEPTION_KEY)); // (2)
}
}
Sr.No. | Description |
---|---|
(1) |
Implement |
(2) |
Get error information from |
Difference in behavior of ChunkListener due to difference in processing model
In the chunk model, handling is not performed by the afterChunkError method because exceptions caused by opening / closing resources are outside the scope captured by the ChunkListener interface. A schematic diagram is shown below. Schematic diagram of exception handling in chunk model
In the tasklet model, exceptions caused by opening and closing resources are handled by the afterChunkError method because they are within the scope captured by the ChunkListener interface. A schematic diagram is shown below. Schematic diagram of exception handling in the tasklet model
If you wish to handle exceptions unifiedly by absorbing this behavior difference,
it can be implemented by checking the occurrence of an exception in the StepExecutionListener interface.
However, the implementation is slightly more complicated than Example of StepExecutionListener implementation.
|
Exception handling in chunk model
In the chunk model, exception handling is done with an inherited Listener StepListener.
For details on how to set listener, please refer Listener setting.
Coding point(ItemReader)
By implementing onReadError
method of ItemReadListener interface,
exceptions raised within ItemReader are handled.
@Component
public class CommonItemReadListener implements ItemReadListener<Object> {
private static final Logger logger =
LoggerFactory.getLogger(CommonItemReadListener.class);
// omitted.
// (1)
@Override
public void onReadError(Exception ex) {
logger.error("Exception occurred while reading.", ex); // (2)
}
// omitted.
}
Sr.No. | Description |
---|---|
(1) |
Implement |
(2) |
Implement exception handling. |
Coding point (ItemProcessor)
There are two ways to handle exception in ItemProcessor, and use it according to requirements.
-
How to try ~ catch in ItemProcessor
-
Using ItemProcessListener interface.
Why they are used properly is explained.
The argument of the onProcessError
method executed when an exception occurs in ItemProcessor processing consists of two items - items to be processed and exceptions to be processed.
Depending on the requirements of the system, when handling exceptions such as log output in the ItemProcessListener
interface, these two arguments may not satisfy the requirement.
In that case, it is recommended to catch the exception with try ~ catch in ItemProcessor and perform exception handling processing.
Note that implementing try ~ catch in ItemProcessor and implementing the ItemProcessListener
interface may result in double processing, so care must be taken.
If fine-grained exception handling is to be done, then adopt a method to try ~ catch in ItemProcessor.
Each method is explained below.
- How to try ~ catch in ItemProcessor
-
This is used to do fine-grained exception handling.
As explained in the skip section below,it will be used when doing error record of Skip.
@Component
public class AmountCheckProcessor implements
ItemProcessor<SalesPerformanceDetail, SalesPerformanceDetail> {
// omitted.
@Override
public SalesPerformanceDetail process(SalesPerformanceDetail item)
throws Exception {
// (1)
try {
checkAmount(item.getAmount(), amountLimit);
} catch (ArithmeticException ae) {
// (2)
logger.error(
"Exception occurred while processing. [item:{}]", item, ae);
// (3)
throw new IllegalStateException("check error at processor.", ae);
}
return item;
}
}
Sr.No. | Description |
---|---|
(1) |
Implement |
(2) |
Implement exception handling. |
(3) |
Throw a transaction rollback exception. |
- How to use the
ItemProcessListener
interface -
Use this,if business exceptions can be handled in the same way.
@Component
public class CommonItemProcessListener implements ItemProcessListener<Object, Object> {
private static final Logger logger =
LoggerFactory.getLogger(CommonItemProcessListener.class);
// omitted.
// (1)
@Override
public void onProcessError(Object item, Exception e) {
// (2)
logger.error("Exception occurred while processing. [item:{}]", item, e);
}
// omitted.
}
Sr.No. | Description |
---|---|
(1) |
Implement |
(2) |
Implement exception handling. |
Coding point(ItemWriter)
By implementing the onWriteError
method of ItemWriteListener interface
exceptions raised within ItemWriter are handled.
@Component
public class CommonItemWriteListener implements ItemWriteListener<Object> {
private static final Logger logger =
LoggerFactory.getLogger(CommonItemWriteListener.class);
// omitted.
// (1)
@Override
public void onWriteError(Exception ex, List item) {
// (2)
logger.error("Exception occurred while processing. [items:{}]", item, ex);
}
// omitted.
}
Sr.No. | Description |
---|---|
(1) |
Implement |
(2) |
Implement exception handling. |
Exception handling in tasklet model
Implement exception handling of tasklet model on its own in tasklet.
When performing transaction processing, be sure to throw the exception again in order to roll back.
@Component
public class SalesPerformanceTasklet implements Tasklet {
private static final Logger logger =
LoggerFactory.getLogger(SalesPerformanceTasklet.class);
// omitted.
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
// (1)
try {
reader.open(chunkContext.getStepContext().getStepExecution()
.getExecutionContext());
List<SalesPerformanceDetail> items = new ArrayList<>(10);
SalesPerformanceDetail item = null;
do {
// Pseudo operation of ItemReader
// omitted.
// Pseudo operation of ItemProcessor
checkAmount(item.getAmount(), amountLimit);
// Pseudo operation of ItemWriter
// omitted.
} while (item != null);
} catch (Exception e) {
logger.error("exception in tasklet.", e); // (2)
throw e; // (3)
} finally {
try {
reader.close();
} catch (Exception e) {
// do nothing.
}
}
return RepeatStatus.FINISHED;
}
}
Sr.No. | Description |
---|---|
(1) |
Implement |
(2) |
Implement exception handling. |
(3) |
Throw the exception again to roll back the transaction. |
Job-level exception handling
Exception handling method on a job level is explained.
It is a common handling method for chunk model and tasklet model.
Implement errors such as system exception and fatal error etc. in job level JobExecutionListener interface.
In order to collectively define exception handling processing, handling is performed on a job level without defining handling processing for each step.
In the exception handling here, do output log and setting ExitCode, do not implement transaction processing.
Prohibition of transaction processing
The processing performed by |
Here, an example of handling an exception when occurs in ItemProcessor is shown. For details on how to set the listener,please refer Listener setting.
@Component
public class AmountCheckProcessor implements
ItemProcessor<SalesPerformanceDetail, SalesPerformanceDetail> {
// omitted.
private StepExecution stepExecution;
// (1)
@BeforeStep
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
@Override
public SalesPerformanceDetail process(SalesPerformanceDetail item)
throws Exception {
// (2)
try {
checkAmount(item.getAmount(), amountLimit);
} catch (ArithmeticException ae) {
// (3)
stepExecution.getExecutionContext().put("ERROR_ITEM", item);
// (4)
throw new IllegalStateException("check error at processor.", ae);
}
return item;
}
}
@Component
public class JobErrorLoggingListener implements JobExecutionListener {
private static final Logger logger =
LoggerFactory.getLogger(JobErrorLoggingListener.class);
@Override
public void beforeJob(JobExecution jobExecution) {
// do nothing.
}
// (5)
@Override
public void afterJob(JobExecution jobExecution) {
// whole job execution
List<Throwable> exceptions = jobExecution.getAllFailureExceptions(); // (6)
// (7)
if (exceptions.isEmpty()) {
return;
}
// (8)
logger.info("This job has occurred some exceptions as follow. " +
"[job-name:{}] [size:{}]",
jobExecution.getJobInstance().getJobName(), exceptions.size());
for (Throwable th : exceptions) {
logger.error("exception has occurred in job.", th);
}
// (9)
for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
Object errorItem = stepExecution.getExecutionContext()
.get("ERROR_ITEM"); // (10)
if (errorItem != null) {
logger.error("detected error on this item processing. " +
"[step:{}] [item:{}]", stepExecution.getStepName(),
errorItem);
}
}
}
}
Sr.No. | Description |
---|---|
(1) |
In order to output error data with |
(2) |
Implement |
(3) |
Implement exception handling. |
(4) |
Throw an exception to do exception handling with |
(5) |
Implement exception handling in |
(6) |
Fetch error information which have occurred in all the jobs, from the argument of |
(7) |
If there is no error information, it is determined as normal termination. |
(8) |
If there is error information, exception handling is performed. |
(9) |
In this example, log output is performed when error data exists. |
Object to be stored in ExecutionContext
The object to be stored in |
Determination as to whether processing can be continued
How to decide whether or not to continue processing jobs when an exception occurs is explained.
Skip
A method of skipping an erroneous record and continuing processing is described.
Chunk model
In the chunk model, the implementation method differs for components of each processing
Always read About reason why <skippable-exception-classes> is not used before applying the contents described here. |
- Skip with ItemReader
-
Specify the skip method in
skip-policy
attribute of<batch:chunk>
. In<batch:skippable-exception-classes>
, specify the exception class to be skipped which occurs in the ItemReader.
For theskip-policy
attribute, use one of the following classes provided by Spring Batch.
Class name | Description |
---|---|
Always skip. |
|
Do not skip. |
|
Skip until the upper limit of the specified number of skips is reached. This is the skipping method used by default when |
|
Use this when you want to change |
Implementation example of skipping is explained.
Handle case where an incorrect record exists when reading a CSV file with FlatFileItemReader
.
The following exceptions occur at this time.
-
org.springframework.batch.item.ItemReaderException
(Base exception class)-
org.springframework.batch.item.file.FlatFileParseException
(Exception occured class)
-
skip-policy
shows how to define it separately.
<bean id="detailCSVReader"
class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="resource" value="file:#{jobParameters['inputFile']}"/>
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer"
p:names="branchId,year,month,customerId,amount"/>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper"
p:targetType="org.terasoluna.batch.functionaltest.app.model.performance.SalesPerformanceDetail"/>
</property>
</bean>
</property>
</bean>
- AlwaysSkipItemSkipPolicy
<!-- (1) -->
<bean id="skipPolicy"
class="org.springframework.batch.core.step.skip.AlwaysSkipItemSkipPolicy"/>
<batch:job id="jobSalesPerfAtSkipAllReadError" job-repository="jobRepository">
<batch:step id="jobSalesPerfAtSkipAllReadError.step01">
<batch:tasklet transaction-manager="jobTransactionManager">
<batch:chunk reader="detailCSVReader"
processor="amountCheckProcessor"
writer="detailWriter" commit-interval="10"
skip-policy="skipPolicy"> <!-- (2) -->
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
Sr.No. | Description |
---|---|
(1) |
Define |
(2) |
Set the bean defined in (1) to the |
- NeverSkipItemSkipPolicy
<!-- (1) -->
<bean id="skipPolicy"
class="org.springframework.batch.core.step.skip.NeverSkipItemSkipPolicy"/>
<batch:job id="jobSalesPerfAtSkipNeverReadError" job-repository="jobRepository">
<batch:step id="jobSalesPerfAtSkipNeverReadError.step01">
<batch:tasklet transaction-manager="jobTransactionManager">
<batch:chunk reader="detailCSVReader"
processor="amountCheckProcessor"
writer="detailWriter" commit-interval="10"
skip-policy="skipPolicy"> <!-- (2) -->
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
Sr.No. | Description |
---|---|
(1) |
Define |
(2) |
Set the bean defined in (1) to the |
- LimitCheckingItemSkipPolicy
(1)
<!--
<bean id="skipPolicy"
class="org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy"/>
-->
<batch:job id="jobSalesPerfAtValidSkipReadError" job-repository="jobRepository">
<batch:step id="jobSalesPerfAtValidSkipReadError.step01">
<batch:tasklet transaction-manager="jobTransactionManager">
<batch:chunk reader="detailCSVReader"
processor="amountCheckProcessor"
writer="detailWriter" commit-interval="10"
skip-limit="2"> <!-- (2) -->
<!-- (3) -->
<batch:skippable-exception-classes>
<!-- (4) -->
<batch:include
class="org.springframework.batch.item.ItemReaderException"/>
</batch:skippable-exception-classes>
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
Sr.No. | Description |
---|---|
(1) |
Define |
(2) |
Set the upper limit value of skip count in the |
(3) |
Define |
(4) |
Set |
- ExceptionClassifierSkipPolicy
<!-- (1) -->
<bean id="skipPolicy"
class="org.springframework.batch.core.step.skip.ExceptionClassifierSkipPolicy">
<property name="policyMap">
<map>
<!-- (2) -->
<entry key="org.springframework.batch.item.ItemReaderException"
value-ref="alwaysSkip"/>
</map>
</property>
</bean>
<!-- (3) -->
<bean id="alwaysSkip"
class="org.springframework.batch.core.step.skip.AlwaysSkipItemSkipPolicy"/>
<batch:job id="jobSalesPerfAtValidNolimitSkipReadError"
job-repository="jobRepository">
<batch:step id="jobSalesPerfAtValidNolimitSkipReadError.step01">
<batch:tasklet transaction-manager="jobTransactionManager">
<!-- skip-limit value is dummy. -->
<batch:chunk reader="detailCSVReader"
processor="amountCheckProcessor"
writer="detailWriter" commit-interval="10"
skip-policy="skipPolicy"> <!-- (4) -->
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
Sr.No. | Description |
---|---|
(1) |
Define |
(2) |
Set the |
(3) |
Define the skipping method you want to execute by exception. |
(4) |
Set the bean defined in (1) to the |
- Skip on ItemProcessor
-
Try ~ catch in ItemProcessor and return null.
Skip withskip-policy
is not used because reprocessing occurs in ItemProcessor. For details, please refer About reason why <skippable-exception-classes> is not used.
Restrictions on exception handling in ItemProcessor
As in About reason why <skippable-exception-classes> is not used,
In ItemProcessor, skipping using |
Implementation example of skip.
Implementation example of try~catch in ItemProcessor of Coding point (ItemProcessor) correspond to skip.
@Component
public class AmountCheckProcessor implements
ItemProcessor<SalesPerformanceDetail, SalesPerformanceDetail> {
// omitted.
@Override
public SalesPerformanceDetail process(SalesPerformanceDetail item) throws Exception {
// (1)
try {
checkAmount(item.getAmount(), amountLimit);
} catch (ArithmeticException ae) {
logger.warn("Exception occurred while processing. Skipped. [item:{}]",
item, ae); // (2)
return null; // (3)
}
return item;
}
}
Sr.No. | Description |
---|---|
(1) |
Implement |
(2) |
Implement exception handling |
(3) |
Skip error data by returning null. |
- Skip with ItemWriter
-
In ItemWriter skip processing is not done generally.
Even when skipping is necessary, skipping byskip-policy
will not be used as the chunk size will change. For details, please refer About reason why <skippable-exception-classes> is not used.
Tasket model
Handle exceptions in business logic and implement processing to skip error records independently.
Implementation example of Exception handling in tasklet model corresponds to skip.
@Component
public class SalesPerformanceTasklet implements Tasklet {
private static final Logger logger =
LoggerFactory.getLogger(SalesPerformanceTasklet.class);
// omitted.
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
// (1)
try {
reader.open(chunkContext.getStepContext().getStepExecution()
.getExecutionContext());
List<SalesPerformanceDetail> items = new ArrayList<>(10);
SalesPerformanceDetail item = null;
do {
// Pseudo operation of ItemReader
// omitted.
// Pseudo operation of ItemProcessor
checkAmount(item.getAmount(), amountLimit);
// Pseudo operation of ItemWriter
// omitted.
} while (item != null);
} catch (Exception e) {
logger.warn("exception in tasklet. Skipped.", e); // (2)
continue; // (3)
} finally {
try {
reader.close();
} catch (Exception e) {
// do nothing.
}
}
return RepeatStatus.FINISHED;
}
}
Sr.No. | Description |
---|---|
(1) |
Implement |
(2) |
Implement exception handling. |
(3) |
Processing of error data is skipped by continue. |
Retry
When an exception is detected, a method of reprocessing until the specified number of times is reached is described.
For retry, it is necessary to consider various factors such as the presence or absence of state management and the situation where retry occurs, there is no reliable method, and retrying it unnecessarily deteriorates the situation.
Therefore, this guideline explains how to use org.springframework.retry.support.RetryTemplate
which implements a local retry.
As with skipping method, a method which specifies the target exception class with |
public class RetryableAmountCheckProcessor implements
ItemProcessor<SalesPerformanceDetail, SalesPerformanceDetail> {
// omitted.
// (1)
private RetryPolicy retryPolicy;
@Override
public SalesPerformanceDetail process(SalesPerformanceDetail item)
throws Exception {
// (2)
RetryTemplate rt = new RetryTemplate();
if (retryPolicy != null) {
rt.setRetryPolicy(retryPolicy);
}
try {
// (3)
rt.execute(new RetryCallback<SalesPerformanceDetail, Exception>() {
@Override
public SalesPerformanceDetail doWithRetry(RetryContext context) throws Exception {
logger.info("execute with retry. [retry-count:{}]", context.getRetryCount());
// retry mocking
if (context.getRetryCount() == adjustTimes) {
item.setAmount(item.getAmount().divide(new BigDecimal(10)));
}
checkAmount(item.getAmount(), amountLimit);
return null;
}
});
} catch (ArithmeticException ae) {
// (4)
throw new IllegalStateException("check error at processor.", ae);
}
return item;
}
public void setRetryPolicy(RetryPolicy retryPolicy) {
this.retryPolicy = retryPolicy;
}
}
<!-- omitted -->
<bean id="amountCheckProcessor"
class="org.terasoluna.batch.functionaltest.ch06.exceptionhandling.RetryableAmountCheckProcessor"
scope="step"
p:retryPolicy-ref="retryPolicy"/> <!-- (5) -->
<!-- (6) (7) (8)-->
<bean id="retryPolicy" class="org.springframework.retry.policy.SimpleRetryPolicy"
c:maxAttempts="3"
c:retryableExceptions-ref="exceptionMap"/>
<!-- (9) -->
<util:map id="exceptionMap">
<entry key="java.lang.ArithmeticException" value="true"/>
</util:map>
<batch:job id="jobSalesPerfWithRetryPolicy" job-repository="jobRepository">
<batch:step id="jobSalesPerfWithRetryPolicy.step01">
<batch:tasklet transaction-manager="jobTransactionManager">
<batch:chunk reader="detailCSVReader"
processor="amountCheckProcessor"
writer="detailWriter" commit-interval="10"/>
</batch:tasklet>
</batch:step>
</batch:job>
Sr.No. | Description |
---|---|
(1) |
Store the retry condition. |
(2) |
Create an instance of RetryTemplate. |
(3) |
Use the |
(4) |
Exception handling when the number of retries exceeds the specified number of times |
(5) |
Specify the retry condition defined in (6). |
(6) |
Define the retry condition in the class that implements |
(7) |
Specify the number of retries in the |
(8) |
Specify the map that defines the target exception to be retried defined in (9) in |
(9) |
Define a map wherein exception class to be retried is set in key and truth value is set in value. |
Process interruption
If you want to abort step execution, throw RuntimeException or its subclass other than skip / retry object.
Implementation example of skip is shown based on [Ch06_ExceptionHandling_HowToUse_ContinuationPropriety_Skip_Chunk_LimitCheckingItemSkipPolicy]
<batch:job id="jobSalesPerfAtValidSkipReadError" job-repository="jobRepository">
<batch:step id="jobSalesPerfAtValidSkipReadError.step01">
<batch:tasklet transaction-manager="jobTransactionManager">
<batch:chunk reader="detailCSVReader"
processor="amountCheckProcessor"
writer="detailWriter" commit-interval="10"
skip-limit="2">
<batch:skippable-exception-classes>
<!-- (1) -->
<batch:include class="org.springframework.batch.item.validator.ValidationException"/>
</batch:skippable-exception-classes>
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
Sr.No. | Description |
---|---|
(1) |
If an exception other than |
An implementation example of retry is shown based on Retry.
<!-- omitted -->
<bean id="retryPolicy" class="org.springframework.retry.policy.SimpleRetryPolicy"
c:maxAttempts="3"
c:retryableExceptions-ref="exceptionMap"/>
<util:map id="exceptionMap">
<!-- (1) -->
<entry key="java.lang.UnsupportedOperationException" value="true"/>
</util:map>
<batch:job id="jobSalesPerfWithRetryPolicy" job-repository="jobRepository">
<batch:step id="jobSalesPerfWithRetryPolicy.step01">
<batch:tasklet transaction-manager="jobTransactionManager">
<batch:chunk reader="detailCSVReader"
processor="amountCheckProcessor"
writer="detailWriter" commit-interval="10"/>
</batch:tasklet>
</batch:step>
</batch:job>
Sr.No. | Description |
---|---|
(1) |
If an exception other than |
Appendix
About reason why <skippable-exception-classes> is not used
Spring Batch provides a function to specify an exception to be skipped for the entire job, skip processing the item where the exception occurred, and continue the processing.
It implements the function by setting the <skippable-exception-classes>
tag under the <chunk>
tag and specifying the exception to be skipped as follows.
<job id="flowJob">
<step id="retryStep">
<tasklet>
<chunk reader="itemReader" writer="itemWriter"
processor="itemProcessor" commit-interval="20"
skip-limit="10">
<skippable-exception-classes>
<!-- specify exceptions to the skipped -->
<include class="java.lang.Exception"/>
<exclude class="java.lang.NullPointerException"/>
</skippable-exception-classes>
</chunk>
</tasklet>
</step>
</job>
By using this function, it is possible to skip the record where the input check error has occurred and continue processing the subsequent data. For TERASOLUNA Batch 5.x, it is not used for the following reasons.
-
If an exception is skipped using the
<skippable-exception-classes>
tag, since the number of data items included in one chunk varies, performance deterioration may occur.-
This depends on where the exception occurred (
ItemReader
/ItemProcessor
/ItemWriter
). Details are described later.
-
Avoid using SkipPolicy without defining <skippable-exception-classes>
All exceptions are implicitly registered, and the possibility of performance degradation increases dramatically. |
The behavior of each exception occurrence ( ItemReader
/ ItemProcessor
/ ItemWriter
) is explained respectively.
The transaction operation is not processed regardless of where the exception occurred, but if an exception occurs, it is always rolled back and then processed again.
- When an exception occurs in ItemReader
-
-
When an exception occurs in the process of
ItemReader
, the processing object moves to the next item. -
There are no side effects.
-
- When an exception occurs in ItemProcessor
-
-
If an exception occurs within the processing of
ItemProcessor
, return to the beginning of the chunk and reprocess from the first. -
Items to be skipped for reprocessing are not included.
-
The chunk size at the first processing and reprocessing does not change.
-
- When an exception occurs in ItemWriter
-
-
If an exception occurs within the processing of
ItemWriter
, return to the beginning of the chunk and reprocess from the first. -
Reprocessing is fixed to
ChunkSize=1
and executed one by one. -
Items to be skipped for reprocessing are also included.
-
If an exception occurs in ItemProcessor
, considering the case of ChunkSize=1000
as an example,
when an exception occurs on the 1000th case, reprocessing is done from the 1st and total of 1999 processes are executed.
If an exception occurs in ItemWriter
, ChunkSize=1
is fixed and reprocessed.
Considering the case of ChunkSize = 1000
as an example,
it is divided into 1000 transactions regardless of originally 1 transaction and processed.
This means that the processing time of the entire job is prolonged, and the situation is highly likely to deteriorate at the time of abnormality. In addition, the double treatment can become a problem, and additional considerations must be employed for design manufacturing.
Therefore, we do not recommend using <skippable-exception-classes>
.
Skipping data that failed in ItemReader
does not cause these problems,
in order to prevent accidents, basically avoid it and apply it only when it is absolutely necessary.