kotlin_geek
kotlin_geek

Reputation: 29

Error occur when i creating spring batch multiple JobBuild

I'm using kotlin, spring boot 3.0.1 and batch . I create two of job/step configuration. but when i build the project, i've saw the below errors. I'm asking a question because it's hard to change the previous batch example to the latest version. Has anyone solved this problem? Strangely enough, if you take one job out on config, it can running it.

errors

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2023-01-03T13:47:34.101+09:00 ERROR 68470 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jobLauncherApplicationRunner': Invocation of init method failed
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:195) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:420) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1743) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:961) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:915) ~[spring-context-6.0.3.jar:6.0.3]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[spring-context-6.0.3.jar:6.0.3]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[spring-boot-3.0.1.jar:3.0.1]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) ~[spring-boot-3.0.1.jar:3.0.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-3.0.1.jar:3.0.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[spring-boot-3.0.1.jar:3.0.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[spring-boot-3.0.1.jar:3.0.1]
    at com.linetest.batch.SpringBatchApplicationKt.main(SpringBatchApplication.kt:15) ~[main/:na]
Caused by: java.lang.IllegalArgumentException: Job name must be specified in case of multiple jobs
    at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.validate(JobLauncherApplicationRunner.java:116) ~[spring-boot-autoconfigure-3.0.1.jar:3.0.1]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:424) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:368) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:192) ~[spring-beans-6.0.3.jar:6.0.3]
    ... 17 common frames omitted

config/SingleStepJobConfig.kt


import com.linetest.batch.tasklet.SimpleTasklet
import mu.KotlinLogging
import org.springframework.batch.core.Job
import org.springframework.batch.core.Step
import org.springframework.batch.core.StepContribution
import org.springframework.batch.core.job.builder.JobBuilder
import org.springframework.batch.core.repository.JobRepository
import org.springframework.batch.core.scope.context.ChunkContext
import org.springframework.batch.core.step.builder.StepBuilder
import org.springframework.batch.repeat.RepeatStatus
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.transaction.PlatformTransactionManager

private val log = KotlinLogging.logger {}

@Configuration
class SingleStepJobConfig(
    private val transactionManager: PlatformTransactionManager,
    private val jobRepository: JobRepository,
) {

    @Bean
    fun singleStepJob(): Job {
        return JobBuilder("singleStepJob", jobRepository)
            .start(singleStep())
            .build()
    }


    @Bean
    fun singleStep(): Step {
        return StepBuilder("singleStep", jobRepository)
            .tasklet(myTaskletAsBean(), transactionManager)
            .build()
    }

    @Bean
    fun singleStep2(): Step {
        return StepBuilder("singleStep2", jobRepository)
            .tasklet({
                    _: StepContribution,
                    _: ChunkContext ->
                log.info { "single step2 tasklet 2 test" }
                RepeatStatus.FINISHED
            }, transactionManager)
            .build()
    }

    @Bean
    fun myTaskletAsBean(): SimpleTasklet {
        val simpleTasklet = SimpleTasklet()
        simpleTasklet.setValue("Single Step Job Properties Setup!!!<<--")

        return simpleTasklet
    }

}

config/MultipleStepJobConfig.kt


import com.linetest.batch.tasklet.SimpleTasklet
import mu.KotlinLogging
import org.apache.logging.log4j.util.Strings
import org.springframework.batch.core.Job
import org.springframework.batch.core.Step
import org.springframework.batch.core.StepContribution
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing
import org.springframework.batch.core.job.builder.JobBuilder
import org.springframework.batch.core.repository.JobRepository
import org.springframework.batch.core.scope.context.ChunkContext
import org.springframework.batch.core.step.builder.StepBuilder
import org.springframework.batch.repeat.RepeatStatus
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.batch.BatchProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.transaction.PlatformTransactionManager

private val log = KotlinLogging.logger {}

@Configuration
class MultipleStepJobConfig(
    private val transactionManager: PlatformTransactionManager,
    private val jobRepository: JobRepository,
) {

    @Bean
    fun multipleStepJob(): Job {
        return JobBuilder("multipleStepJob", jobRepository)
            .start(startStep())
            .next(processStep())
            .next(writeStep())
            .build()
    }

    @Bean
    fun startStep(): Step {
        return StepBuilder("startStep", jobRepository)
            .tasklet({
                     _: StepContribution,
                    _: ChunkContext ->
                log.info { "multiple Step Start" }
                RepeatStatus.FINISHED
            }, transactionManager)
            .build()
    }



    @Bean
    fun processStep(): Step {
        return StepBuilder("processStep", jobRepository)
            .tasklet({_: StepContribution,
                      _: ChunkContext ->
                log.info { "Execute processStep >>>>" }
                RepeatStatus.FINISHED
            }, transactionManager)
            .build()
    }

    @Bean
    fun writeStep(): Step {
        return StepBuilder("writeStep", jobRepository)
            .tasklet({_: StepContribution,
                      _: ChunkContext ->
                log.info { "Execute writeStep >>>>" }
                RepeatStatus.FINISHED
            }, transactionManager)
            .build()
    }



}

tasklet/SimpleTasklet.kt

import mu.KotlinLogging
import org.springframework.batch.core.StepContribution
import org.springframework.batch.core.configuration.annotation.StepScope
import org.springframework.batch.core.scope.context.ChunkContext
import org.springframework.batch.core.step.tasklet.Tasklet
import org.springframework.batch.repeat.RepeatStatus
import org.springframework.beans.factory.InitializingBean
import org.springframework.stereotype.Component

private val log = KotlinLogging.logger {  }

@Component
@StepScope
class SimpleTasklet : Tasklet, InitializingBean {
    private var resource: String? = null

    @Throws(Exception::class)
    override fun execute(
        contribution: StepContribution,
        chunkContext: ChunkContext
    ): RepeatStatus? {
        log.info { "Execute SimpleTasklet >>>>" }
        return RepeatStatus.FINISHED
    }

    fun setValue(text:String){
        this.resource = text
        log.info { "$resource"}
    }

    @Throws(Exception::class)
    override fun afterPropertiesSet() {
        log.info { "AfterPropertiesSet" }
    }

}

SpringBatchApplication.kt

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class SpringBatchApplication

fun main(args: Array<String>) {
    runApplication<SpringBatchApplication>(*args)
}

Strangely enough, if you take one job out on config, it can running it.

Existing Example can running. ref. https://www.devkuma.com/docs/spring-batch/start/

the result spring boot 2.5.5

2023-01-04 00:05:48.365  INFO 7829 --- [           main] com.linetest.batch.BatchApplicationKt    : Starting BatchApplicationKt using Java 17.0.5 on mC02YR40ALVDL with PID 7829 (/Users/ParkJiho/Documents/workspace/kotlin/spring-batch-test-0102/build/classes/kotlin/main started by ParkJiho in /Users/ParkJiho/Documents/workspace/kotlin/spring-batch-test-0102)
2023-01-04 00:05:48.366  INFO 7829 --- [           main] com.linetest.batch.BatchApplicationKt    : No active profile set, falling back to default profiles: default
2023-01-04 00:05:49.267  INFO 7829 --- [           main] c.linetest.batch.tasklet.SimpleTasklet   : TESTTESTTEST  has been set!!!!!!!!!!1
2023-01-04 00:05:49.267  INFO 7829 --- [           main] c.linetest.batch.tasklet.SimpleTasklet   : AfterPropertiesSet
2023-01-04 00:05:49.282  INFO 7829 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2023-01-04 00:05:49.433  INFO 7829 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2023-01-04 00:05:49.538  INFO 7829 --- [           main] o.s.b.c.r.s.JobRepositoryFactoryBean     : No database type set, using meta data indicating: H2
2023-01-04 00:05:49.665  INFO 7829 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : No TaskExecutor has been set, defaulting to synchronous executor.
2023-01-04 00:05:49.722  INFO 7829 --- [           main] com.linetest.batch.BatchApplicationKt    : Started BatchApplicationKt in 1.659 seconds (JVM running for 2.351)
2023-01-04 00:05:49.723  INFO 7829 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2023-01-04 00:05:49.766  INFO 7829 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=flowStepJob]] launched with the following parameters: [{}]
2023-01-04 00:05:49.799  INFO 7829 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [flowStartStep]
2023-01-04 00:05:49.806  INFO 7829 --- [           main] c.l.batch.config.FlowStepJobConfig       : Flow Start Step!!<------
2023-01-04 00:05:49.811  INFO 7829 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [flowStartStep] executed in 12ms
2023-01-04 00:05:49.816  INFO 7829 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [flowProcessStep]
2023-01-04 00:05:49.818  INFO 7829 --- [           main] c.l.batch.config.FlowStepJobConfig       : flowProcessStep
2023-01-04 00:05:49.819  INFO 7829 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [flowProcessStep] executed in 3ms
2023-01-04 00:05:49.823  INFO 7829 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [flowWriteStep]
2023-01-04 00:05:49.825  INFO 7829 --- [           main] c.l.batch.config.FlowStepJobConfig       : flowWriteStep
2023-01-04 00:05:49.827  INFO 7829 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [flowWriteStep] executed in 3ms
2023-01-04 00:05:49.830  INFO 7829 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=flowStepJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 40ms
2023-01-04 00:05:49.833  INFO 7829 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=multipleStepJob]] launched with the following parameters: [{}]
2023-01-04 00:05:49.837  INFO 7829 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [startStep]
2023-01-04 00:05:49.839  INFO 7829 --- [           main] c.l.batch.config.MultipleStepJobConfig   : Start Step<--------
2023-01-04 00:05:49.841  INFO 7829 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [startStep] executed in 4ms
2023-01-04 00:05:49.844  INFO 7829 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [nextStep]
2023-01-04 00:05:49.846  INFO 7829 --- [           main] c.l.batch.config.MultipleStepJobConfig   : Next Step<--------
2023-01-04 00:05:49.847  INFO 7829 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [nextStep] executed in 3ms
2023-01-04 00:05:49.850  INFO 7829 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [lastStep]
2023-01-04 00:05:49.852  INFO 7829 --- [           main] c.l.batch.config.MultipleStepJobConfig   : Last Step<--------
2023-01-04 00:05:49.853  INFO 7829 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [lastStep] executed in 3ms
2023-01-04 00:05:49.855  INFO 7829 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=multipleStepJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 21ms
2023-01-04 00:05:49.858  INFO 7829 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=singleStepJob]] launched with the following parameters: [{}]
2023-01-04 00:05:49.861  INFO 7829 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [simpleStep]
2023-01-04 00:05:49.862  INFO 7829 --- [           main] c.linetest.batch.tasklet.SimpleTasklet   : Execute SimpleTasklet >>>>
2023-01-04 00:05:49.864  INFO 7829 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [simpleStep] executed in 3ms
2023-01-04 00:05:49.865  INFO 7829 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=singleStepJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 7ms
2023-01-04 00:05:49.869  INFO 7829 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2023-01-04 00:05:49.871  INFO 7829 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

Upvotes: 2

Views: 7931

Answers (1)

Rohit Agarwal
Rohit Agarwal

Reputation: 978

The issue very clear from logs.

Caused by: java.lang.IllegalArgumentException: Job name must be specified in case of multiple jobs
    at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.validate(JobLauncherApplicationRunner.java:116) ~[spring-boot-autoconfigure-3.0.1.jar:3.0.1]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:424) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:368) ~[spring-beans-6.0.3.jar:6.0.3]
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:192) ~[spring-beans-6.0.3.jar:6.0.3]
    ... 17 common frames omitted

If only one job is present in Spring Context then Spring run that job by default on startup but If multiple jobs are present in Spring Context then we need to tell Spring which job to run on startup.

So you can specify this property in application.properties file and try again.

spring.batch.job.name=singleStepJob

or

spring.batch.job.name=multipleStepJob

or you can disable running job on startup by this property and run the job programmatically using JobLauncher.

spring.batch.job.enabled=false

How to run job programmatically?

public class JobLauncherService {

    private final JobLauncher jobLauncher;
    
    private final Job job;

    @Autowired
    public JobLauncherService(JobLauncher jobLauncher, @Qualifier("multipleStepJob") Job job) {
        this.jobLauncher = jobLauncher;
        this.job = job;
    }

    public void runJob() {
        try {
            final JobExecution jobExecution = jobLauncher.run(job, new JobParameters());
            System.out.println("Job Status : " + jobExecution.getStatus());
            System.out.println("Job completed");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Job failed");
        }
    }
}

Upvotes: 9

Related Questions