Joshua
Joshua

Reputation: 355

Spring Batch error (A Job Instance Already Exists) and RunIdIncrementer generates only once

i'm using Spring Batch & Quartz to read from database table and write in another table. the database is Oracle and it is c3p0

the problem is each job must have a unique parameters, I tried RunIdIncrementer and I tried this code:

public class JobRerunner implements JobParametersIncrementer {

    @Override
    public JobParameters getNext(JobParameters parameters) {
        System.out.println("got job parameters: " + parameters);
        if (parameters==null || parameters.isEmpty()) {
            return new JobParametersBuilder().addLong("run.id", System.currentTimeMillis()).toJobParameters();
        }
        long currentTime = parameters.getLong("run.id",System.currentTimeMillis()) + 1;
        return new JobParametersBuilder().addLong("run.id",currentTime).toJobParameters();
    }

}

but I get the same problem, the run.id is generated only once, and when the job is ran for the second time it has no parameters at all and the third time also (the second and third run JobParameter = null so (Job Instance Already Exists)

job context

<batch:job id="readyReqPoolJob" restartable="true">
    <batch:step id="readyReqPoolStep">
        <batch:tasklet>
            <batch:chunk reader="readyReqPoolReader" writer="readyReqPoolWrtiter"
                commit-interval="100" />
        </batch:tasklet>
    </batch:step>
</batch:job>


<!-- ======================================================= -->
<!-- 6) READER -->
<!-- ======================================================= -->
<bean id="readyReqPoolReader"
    class="org.springframework.batch.item.database.JdbcCursorItemReader">
    <property name="dataSource" ref="dataSource" />
    <property name="sql" value="select * from SF_ILA_Ready_Request_Pool" />
    <property name="rowMapper" ref="ReadyReqPoolRowMapper" />
</bean>
<bean id="readyReqPoolWrtiter"
    class="com.housekeepingservice.readyrequestpoolarchive.ReadyReqPoolArchiveWriter" />


<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass"
        value="org.springframework.batch.sample.quartz.JobLauncherDetails" />
    <property name="jobDataAsMap">
        <map>
            <entry key="jobName" value="readyReqPoolJob" />
            <entry key="jobLocator" value-ref="jobRegistry" />
            <entry key="jobLauncher" value-ref="jobLauncher" />
        </map>
    </property>
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <bean id="cronTrigger"
            class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <property name="jobDetail" ref="jobDetail" />
            <property name="cronExpression" value="0 0/5 * * * ?" />
        </bean>
    </property>
</bean>

main context:

<import resource="classpath:spring/batch/config/readyReqPoolContext.xml"
<import resource="classpath:spring/batch/config/jdbc.commons.xml" />
    <!-- 1) USE ANNOTATIONS TO CONFIGURE SPRING BEANS -->
    <context:component-scan base-package="com.housekeepingservice" />


    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean
        class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
        <property name="jobRegistry" ref="jobRegistry" />
    </bean>

    <bean id="jobRegistry"
        class="org.springframework.batch.core.configuration.support.MapJobRegistry" />

    <!-- 3) JOB REPOSITORY  -->

    <bean id="jobRepository" 
        class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
        <property name="transactionManager" ref="transactionManager" />
    </bean>

    <!-- 4) LAUNCH JOBS FROM A REPOSITORY -->
    <bean id="jobLauncher"
        class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
        <property name="jobRepository" ref="jobRepository" />
        <property name="taskExecutor" ref="taskExecutor" />
    </bean>
    <bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" />

    <bean id="jobExplorer"
        class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean">
        <property name="dataSource" ref="dataSource" />
    </bean>
        <bean name="jobParamatersIncrementer" class="org.springframework.batch.core.launch.support.RunIdIncrementer">
    </bean>

Test.java

public class Test {
    public static void main(String[] args) {

        String[] springConfig = { "spring/batch/config/mainContext.xml" };

        ApplicationContext context = new ClassPathXmlApplicationContext(
                springConfig);
        JobRerunner rerun = new JobRerunner();

        JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");

        Job readyRequestPoolJob = (Job) context.getBean("readyReqPoolJob");


        try {


            JobParameters jobParameters = new JobParameters();
             JobExecution execution2 = jobLauncher.run(readyRequestPoolJob, rerun.getNext(jobParameters));

            System.out.println("Exit Status : " + execution2.getStatus());


        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

}

log (checkout the job incetance parameters in the first run and the second run):

17:00:27,053  INFO SimpleJobLauncher:132 - Job: [FlowJob: [name=readyReqPoolJob]] launched with the following parameters: **[{run.id=1393855226339}]**
17:00:27.085 [Timer-0] DEBUG org.quartz.utils.UpdateChecker - Checking for available updated version of Quartz...
17:00:27,272  INFO SimpleStepHandler:135 - Executing step: [readyReqPoolStep]
17:02:08,791  INFO SimpleJobLauncher:135 - Job: [FlowJob: [name=readyReqPoolJob]] completed with the following parameters: [{run.id=1393855226339}] and the following status: [COMPLETED]
17:10:00.005 [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job DEFAULT.jobDetail
17:10:00,008  INFO JobLauncherDetails:69 - Quartz trigger firing with Spring Batch jobName=readyReqPoolJob
17:10:00,036  INFO SimpleJobLauncher:132 - Job: [FlowJob: [name=readyReqPoolJob]] launched with the following parameters: **[{}]**
17:10:00,059  INFO SimpleStepHandler:135 - Executing step: [readyReqPoolStep]

Upvotes: 5

Views: 18317

Answers (3)

Ashraf Sarhan
Ashraf Sarhan

Reputation: 1697

Set step allowStartIfComplete flag to True

Upvotes: 5

Haim Raman
Haim Raman

Reputation: 12043

To lunch a job with job Incremater you need two things

  1. Attach the RunIdIncremater to your job.
  2. Use a launcher that is aware of the usage Incremater.

I do not see any need for your own implementation just use the existing one.
Attach the RunIdIncremater to your job.

<batch:job id="readyReqPoolJob" incrementer="runIdIncrementer" restartable="true">
</batch:job>
<bean id="runIdIncrementer" 
class="org.springframework.batch.core.launch.support.RunIdIncrementer"/>

Use a launcher
To launch it you should use one of the following:
Option 1: CommandLineJobRunner with the –next option see the API

Option 2: User JobOperator

<bean id="jobOperator"
        class="org.springframework.batch.core.launch.support.SimpleJobOperator">
        <property name="jobRepository" ref="jobRepository" />
        <property name="jobLauncher" ref="jobLauncher" />
        <property name="jobRegistry" ref="jobRegistry" />
        <property name="jobExplorer" ref="jobExplorer" />
</bean>

in the code

jobOperator.startNextInstance(jobName)

Option 3: In Junit you can use JobLauncherTestUtils.
Note that it has it’s own id Incremater and will ignore the one you use
see also the following answer SpringBatch: Test a JobExecutionListener

Upvotes: 3

Luca Basso Ricci
Luca Basso Ricci

Reputation: 18413

Add a parameter called 'timestamp' for example or - if you want to use run.id - set Job.jobParametersIncrementer with your jobParamatersIncrementer bean definition.

Upvotes: 1

Related Questions