1

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="[email protected]" />
        <property name="to" value="[email protected]" />
        <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="[email protected]" />
    <property name="to" value="[email protected]" />
    <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

6
  • Can you please include the code you tried to use so we could evaluate and help improve it? :-) Commented Jun 27, 2016 at 12:22
  • @Sometowngeek I have put the code. I hope it helps. Thanks Commented Jun 28, 2016 at 4:02
  • I'm trying to understand your question... Does your question ask to check if the database server is down, or if a part of the database schema is unavailable? Commented Jun 29, 2016 at 13:00
  • @Sometowngeek I have a situation where I have to send mails specifying what went wrong, that caused the job to fail eg. If database was down while reading, so through exception should be able to understand database is down and if table I am trying to read doesn't exist then I should be able to see that exception and send a mail stating job failed coz table I am trying to read doesn't exist. I hope this clarifies your doubt a bit. Please tell if you need any more information. Thanks for your interest and help. Commented Jun 30, 2016 at 6:08
  • That piece is out of my expertise. If you wanted to check to see if the schema or table is down before reading, I could probably help with that. But while reading, I am not sure how to do that. I'm sorry :-\ Commented Jun 30, 2016 at 13:18

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for taking the time out and answering my query. your answers helps but requirement is like this only, that I have to solve it using spring batch only, I can't use other tools to check resources.
Is there some other team to check system health? What if the server itself is down or out of RAM so you can't even start the JVM? What if the command line args are invalid so the job cannot start? What if someone moves that JAR or alters the classpath so it can't be picked up? In any of those scenarios, no amount of code in your job will be able to send an email alert.
Beyond that, if the database is down, you will not be able to create your ApplicationContext, and you will not be able to start your job instance. If that's the case, you'll never hit your afterJob() method in a JobExecutionListener (or afterStep() in any StepExecutionListener), and no email will be sent. Who is providing these requirements?!
1

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

3 Comments

Hi Shankar, I had already tried this but onReadError() doesn't get hit until there is problem in reading, in this case reading has not started, so onReadError will never be notified as it is an ItemReadListener. Thanks.
Then use the StepExecutionListener. In that check for the status in afterStep() method. The stepExexuction.getFailureExceptions() should give you more info to catch the error.
Hi Shankar, that is what I have done, as I mentioned in the question. I was looking for a better way :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.