简体   繁体   中英

How to throw custom exception in spring batch?

I 've come across a scenario where I have to keep track of various exceptions on various conditions in my batch written using spring batch. For eg: If while reading database is not available throw certain type of exception and send a mail stating database is not available and terminate batch. if table is not available then throw some other exception and send a mail stating table is not available and terminate batch. and if data is not meeting the conditions specified in sql statement don't do anything as this is a normal termination of job. All I am able to achieve till now is using StepExecutionListener where I can see if batch read any records or what is the failureException but not in a way I want. Any help/suggestions would do.

My context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:batch="http://www.springframework.org/schema/batch" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <import resource="classpath:context-datasource.xml" />

    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

        <property name="location">
            <value>springbatch.properties</value>
        </property>
    </bean>

    <bean id="validator"
        class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

    <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>


    <!-- ItemReader which reads from database and returns the row mapped by 
        rowMapper -->
    <bean id="databaseItemReader"
        class="org.springframework.batch.item.database.JdbcCursorItemReader">

        <property name="dataSource" ref="dataSource" />

        <property name="sql" value="SELECT * FROM employee1" />

        <property name="rowMapper">
            <bean class="com.abc.springbatch.jdbc.EmployeeRowMapper" />
        </property>

    </bean>


    <!-- ItemWriter writes a line into output flat file -->
    <bean id="databaseItemWriter"
        class="org.springframework.batch.item.database.JdbcBatchItemWriter">

        <property name="dataSource" ref="dataSource" />

        <property name="sql">
            <value>
                <![CDATA[        
                    insert into actemployee(empId, firstName, lastName,additionalInfo) 
                    values (?, ?, ?, ?)
                ]]>
            </value>
        </property>

        <property name="itemPreparedStatementSetter">
            <bean class="com.abc.springbatch.jdbc.EmployeePreparedStatementSetter" />
        </property>

    </bean>


    <!-- Optional ItemProcessor to perform business logic/filtering on the input 
        records -->
    <bean id="itemProcessor" class="com.abc.springbatch.EmployeeItemProcessor">
        <property name="validator" ref="validator" />
    </bean>

    <!-- Step will need a transaction manager -->
    <bean id="transactionManager"
        class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />


    <bean id="recordSkipListener" class="com.abc.springbatch.RecordSkipListener" />

    <bean id="customItemReadListener" class="com.abc.springbatch.CustomItemReadListener" />

    <bean id="stepExecutionListener" class="com.abc.springbatch.BatchStepExecutionListner">
        <constructor-arg ref="mailSender" />
        <constructor-arg ref="preConfiguredMessage" />
    </bean>

    <!-- Actual Job -->
    <batch:job id="employeeToActiveEmployee">
        <batch:step id="step1">
            <batch:tasklet transaction-manager="transactionManager">
                <batch:chunk reader="databaseItemReader" writer="databaseItemWriter"
                    processor="itemProcessor" commit-interval="10" skip-limit="500" retry-limit="5">
                    <batch:listeners>
                        <batch:listener ref="customItemReadListener"/>
                    </batch:listeners>
                    <!-- Retry included here to retry for specified times in case the following exception occurs -->
                    <batch:retryable-exception-classes>
                        <batch:include
                            class="org.springframework.dao.DeadlockLoserDataAccessException" />
                    </batch:retryable-exception-classes>
                    <batch:skippable-exception-classes>
                        <batch:include class="javax.validation.ValidationException" />
                    </batch:skippable-exception-classes>
                </batch:chunk>

            </batch:tasklet>
            <batch:listeners>
                <batch:listener ref="recordSkipListener" />
                <batch:listener ref="stepExecutionListener" />
            </batch:listeners>
        </batch:step>
    </batch:job>




    <!-- Email API bean configuarion -->

    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
        <property name="host" value="${constant.order.mailHost.response}" />
        <property name="port" value="${constant.order.mailPort.response}" />
        <property name="username" value="${constant.order.mailUsername.response}" />
        <property name="password" value="XXXXXX" />
        <property name="javaMailProperties">
            <props>
                <prop key="mail.transport.protocol">smtp</prop>
                <prop key="mail.smtp.auth">false</prop>
                <prop key="mail.smtp.starttls.enable">true</prop>
                <prop key="mail.debug">true</prop>
            </props>
        </property>
    </bean>
    <bean id="preConfiguredMessage" class="org.springframework.mail.SimpleMailMessage">
        <property name="from" value="abc@xyz.com" />
        <property name="to" value="abc@xyz.com" />
        <property name="subject" value="Skipped Records" />
    </bean>


</beans>


<!-- Email API bean configuarion -->

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host" value="${constant.order.mailHost.response}" />
    <property name="port" value="${constant.order.mailPort.response}" />
    <property name="username" value="${constant.order.mailUsername.response}" />
    <property name="password" value="XXXXXX" />
    <property name="javaMailProperties">
        <props>
            <prop key="mail.transport.protocol">smtp</prop>
            <prop key="mail.smtp.auth">false</prop>
            <prop key="mail.smtp.starttls.enable">true</prop>
            <prop key="mail.debug">true</prop>
        </props>
    </property>
</bean>
<bean id="preConfiguredMessage" class="org.springframework.mail.SimpleMailMessage">
    <property name="from" value="abc@xyz.com" />
    <property name="to" value="abc@xyz.com" />
    <property name="subject" value="Skipped Records" />
</bean>

StepExecutionListener.java

public class BatchStepExecutionListner implements StepExecutionListener {

    private JavaMailSender mailSender;

    private SimpleMailMessage simpleMailMessage;

    public BatchStepExecutionListner(JavaMailSender mailSender, SimpleMailMessage preConfiguredMessage) {
        // TODO Auto-generated constructor stub
        this.mailSender = mailSender;
        this.simpleMailMessage = preConfiguredMessage;
    }

    @Override
    public void beforeStep(StepExecution stepExecution) {
        // TODO Auto-generated method stub

    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        // TODO Auto-generated method stub
        stepExecution.getReadCount();
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);

            helper.setFrom(simpleMailMessage.getFrom());
            helper.setTo(simpleMailMessage.getTo());
            helper.setSubject(simpleMailMessage.getSubject());
            helper.setText("These are the skipped records");

            FileSystemResource file = new FileSystemResource("filename.txt");
            helper.addAttachment(file.getFilename(), file);

        } catch (MessagingException e) {
            throw new MailParseException(e);
        }
        //mailSender.send(message);

        return null;
    }

}

Thanks

If the database is down, you'll fail to create the data source as you initialize your application context (well before you enter the job execution). Beyond that, you really should think about limiting the scope of what is "reasonable" to catch within the application. Generally (at least in our shop) a DB failure, network issue, or dropped table would be considered a "catastrophic" failure, so we don't bother catching them in application code.

There should be other tools in place to monitor network/system/database health and configuration management tools in place to make sure your databases have the proper DDL in place. Any further checks in your application layer would be redundant.

The ItemWriteListener has onWriteError() and the ItemReadListener has onReadError() method. This can be used to handle different exceptions and take action.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM