2

I have a Spring Batch job that I am launching from a Spring boot application, like so:

Main:

@SpringBootApplication
@ImportResource("jobApplicationContext.xml")
public class BatchJobRunner {

    public static void main(String[] args) {
        SpringApplication.run(BatchJobRunner.class, args);
    }

}

In my job's application context, I have the following items:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:batch="http://www.springframework.org/schema/batch"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder location="classpath:*.properties"/>

    <bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry"/>
    <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>

    <batch:job id="myJob" job-repository="jobRepository">
        <batch:split id="main" task-executor="simpleAsyncTaskExecutor" next="step3">
            <batch:flow>
                <batch:step id="flow1">
                    <!-- definition -->
                </batch:step>
            </batch:flow>
            <batch:flow>
            <batch:step id="flow2">
                <!-- definition -->
            </batch:step>
            </batch:flow>
        </batch:split>
        <batch:step id="step3">
            <batch:tasklet ref="someTasklet"/>
        </batch:step>
    </batch:job>
</beans>

And finally, I just run it like this:

java -jar my-module.jar

The job starts but:

  • It does not print out anything. Here is my log4j.properties:

    log4j.rootLogger=INFO, stdout
    log4j.logger.org.springframework.batch=INFO
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target=System.out
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
    
  • The job hangs at the end. I put a Sys.out.print in the step3 and it does indeed print, but the spring boot application keeps running and never exits. I also tried to add an @AfterJob with a System.exit(..) and it didn't help either.

I am using Spring f/w 4.1.8, spring boot 1.2.8 and spring batch 3.0.6 (i cannot upgrade my spring-core as some dependencies use that version).

Any idea what I am doing wrong?

Edit:

Looks like beforeJob and afterJob listeners are not firing at all.

Sayak Banerjee
  • 1,954
  • 3
  • 25
  • 58
  • which step it hangs.. do you read from database or somewhere.. – surya Feb 14 '16 at 19:39
  • @surya if you look at my code snippet, i am using an in memory registry. But i am pretty sure it is not stuck at a step. I am able to run the same job when i run it in Spring XD. – Sayak Banerjee Feb 14 '16 at 19:55

2 Answers2

2

ClassCastExeption can be result of Spring different binding (early in xml and late in java). Try configure your batch fully in java. Result can look like this (this is with stored repository in DB, inmemory repository should looks similar):

@Configuration
@EnableBatchProcessing
@ComponentScan("my.batch.*")
@ImportResource("classpath:batch-config.xml")
@PropertySource(value = "classpath:batch.properties")
public class BatchConfiguration implements BatchConfigurer {

@Autowired
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
private JobRepository jobRepository;
private JobLauncher jobLauncher;
private JobExplorer jobExplorer;

@Override
public JobRepository getJobRepository() throws Exception {
    return jobRepository;
}

@Override
public PlatformTransactionManager getTransactionManager() throws Exception {
    return transactionManager;
}

@Override
public JobLauncher getJobLauncher() throws Exception {
    return jobLauncher;
}

@Override
public JobExplorer getJobExplorer() throws Exception {
    return jobExplorer;
}

@PostConstruct
public void initialize() {
    try {
        transactionManager = new DataSourceTransactionManager(dataSource);
        jobRepository = createJobRepository();

        jobExplorer = createJobExplorer();
        jobLauncher = createJobLauncher();
    } catch (Exception ex) {
        throw  new BatchConfigurationException(ex);
    }
}

private JobRepository createJobRepository() throws Exception {
    JobRepositoryFactoryBean repoFactory = new JobRepositoryFactoryBean();

    repoFactory.setDataSource(dataSource);
    repoFactory.setTransactionManager(transactionManager);
    repoFactory.setTablePrefix(PREFIX);

    repoFactory.afterPropertiesSet();

    return repoFactory.getObject();
}

private JobExplorer createJobExplorer() throws Exception {
    JobExplorerFactoryBean explorerFactory = new JobExplorerFactoryBean();

    explorerFactory.setDataSource(dataSource);
    explorerFactory.setTablePrefix(PREFIX);

    explorerFactory.afterPropertiesSet();

    return explorerFactory.getObject();
}

private JobLauncher createJobLauncher() throws Exception {
    SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
    jobLauncher.setJobRepository(jobRepository);
    jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());

    jobLauncher.afterPropertiesSet();

    return jobLauncher;
}
}
rell
  • 151
  • 5
  • 1
    First off, a big thanks for a detailed answer! I tried your solution, and I tried defining a in my XML, both of which give me this error: org.springframework.batch.core.repository.JobExecutionAlreadyRunningException: A job execution for this job is already running I understand that I will have to pass a random parameter to the job to fix this. Do you know how I can do that? – Sayak Banerjee Feb 15 '16 at 01:42
  • OK, so I invoked the job manually from within my spring boot's main() and now at least my job does not get hung (yay!). Now I only need to fix the logging level for the job and find out why nothing is being printed even when my log level is set to info, and all my logger calls print as info. – Sayak Banerjee Feb 15 '16 at 02:06
  • After some digging, I was missing slf4j-simple from my jar, which solved my logging issue. That is needed in addition to slf4j-api. Thank you rell for your answer, I hope you have a good week ahead. – Sayak Banerjee Feb 15 '16 at 02:54
  • it can be helpful https://docs.spring.io/spring-batch/apidocs/org/springframework/batch/core/launch/support/CommandLineJobRunner.html – rell Feb 17 '16 at 09:03
  • Gave @rell a point for motivating SayakBanerjee to use a Java configuration, which has been possible since servlet 2.5, I believe. So many Spring Batch projects still being configured in XML, because of books released in 2011 and online tutorials. In SayakBanerjee's defense, it's hard to find good Java-configured Spring Boot examples – Rick Jul 18 '16 at 20:28
0

You miss @EnableBatchProcessing docs.

rell
  • 151
  • 5
  • No, I didn't miss @EnableBatchProcessing - i didn't want to use it as it tries to auto load everything like the jobRepository and I am unable to override it with my own bean. – Sayak Banerjee Feb 14 '16 at 21:17
  • Without it batch does not run. You must use annotate (see source of SimpleBatchConfiguration and his parent). To override default settings implements **BatchConfigurer** https://docs.spring.io/spring-batch/apidocs/org/springframework/batch/core/configuration/annotation/BatchConfigurer.html – rell Feb 14 '16 at 22:05
  • Thanks you for your response. I did try that too, and when I do so, I get: java.lang.ClassCastException: org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean$$EnhancerBySpringCGLIB$$581089fa cannot be cast to org.springframework.batch.core.repository.JobRepository – Sayak Banerjee Feb 14 '16 at 22:25
  • Add ` ` to your xml. See http://forum.spring.io/forum/spring-projects/batch/94360-classcastexception-proxy9-to-multiresourceitemreader and http://stackoverflow.com/questions/27803053/stepscope-within-spring-batch-throw-exception-when-trying-pass-param – rell Feb 14 '16 at 22:31
  • That didn't solve my issue unfortunately, I still get the same exception. – Sayak Banerjee Feb 14 '16 at 23:03
  • Answer is too long to comment, post another answer. Maybe it help – rell Feb 14 '16 at 23:28