Spring Batch - A job instance already exists: JobInstanceAlreadyCompleteException
Asked Answered
L

5

17

I wrote an easy scheduler included in my Spring Application. I run my local server and after few seconds, in class Importer, checker.start() is being invoked each 5 seconds as how I configured in config file.

After that, this method invokes Job with JobLauncher and here I have got an error.

org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException: A job instance already exists and is complete for p arameters={}. If you want to run this job again, change the parameters.

I found a solution how to fix it using annotation but I want to keep it this way.

Thank you in advance

public class Importer {

    private Checker checker;

    public Importer() {
    }

    public void myMethod() {
        try {
            checker.start(); 
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

with .xml file:

<bean id="schedulerTask"
    class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
    <property name="targetObject" ref="fileimport" />
    <property name="targetMethod" value="myMethod" />
</bean>

<bean id="fileimport" />
    <property name="targetMethod" value" class="com...Importer">
    <property name="checker">
        <bean id="checker" class="com...Checker">

        </bean>
    </property>
</bean>

<bean id="scheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
    <property name="timerTask" ref="schedulerTask" />
    <property name="delay" value="${xyz.startdelay}" />
    <property name="period" value="${xyz.checkinterval}" />
</bean>

<bean class="org.springframework.scheduling.timer.TimerFactoryBean">
    <property name="scheduledTimerTasks">
        <list>
            <ref local="scheduledTask" />
        </list>
    </property>
</bean>

And property file:

xyz.checkinterval=5000

xyz.startdelay=0

In class Checker I have got the method:

static ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext("/simplefile-context.xml");
Job job = (Job) applicationContext.getBean("fileBatch");
JobLauncher launcher = (JobLauncher) applicationContext.getBean("jobLauncher");
public void start() throws ClientProtocolException, IOException {
    // ...
    try {
        launcher.run(job, new JobParameters());
        } catch (Exception e) {
        e.printStackTrace();
    }
}

and my "simplefile-context.xml" file looks like this:

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

<!-- bean for lauching the job -->
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
    <property name="jobRepository" ref="jobRepository" />
</bean>

<task:executor id="taskExecutor" pool-size="100" />

<!--                                   -->
<!--                JOBS               -->
<!--                                   -->
<batch:job id="fileBatch" restartable="true">
    <batch:step id="readLines" >
        <batch:tasklet task-executor="taskExecutor" >
            <batch:chunk reader="fileReader" writer="fooWriter" commit-interval="100" />
        </batch:tasklet>
    </batch:step>
</batch:job>

<bean id="fileReader" class="org.springframework.batch.item.file.FlatFileItemReader">
    <property name="linesToSkip" value="1"/>
    <property name="resource" value="file:./src/main/resources/sample.csv" />
    <property name="lineMapper" ref="lineMapper" />
</bean>

<bean id="lineMapper" class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
    <property name="lineTokenizer" ref="lineTokenizer"/>
    <property name="fieldSetMapper" ref="fieldsetEntityMapper"/>
</bean>

<bean id="lineTokenizer" class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
    <property name="delimiter" value=";" />
    <property name="names" value="field1,field2,field3,field4,field5,field6,field7" />
    <property name="strict" value="false"/>
</bean>

<bean id="fieldsetEntityMapper" class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
    <property name="targetType" value="com...model.ModelObject"/>
    <property name="customEditors">
        <map>
            <entry key="java.lang.Double">
                <bean class="org.springframework.beans.propertyeditors.CustomNumberEditor">
                    <constructor-arg index="0" value="java.lang.Double"/>
                    <constructor-arg index="1" value="true"/>
                </bean>
            </entry>
        </map>
    </property>
</bean>

<bean id="fooWriter" class="com...springbatch.writer.FooWriter" />
Letsou answered 17/3, 2014 at 13:32 Comment(0)
D
35

With your current setup you will only be able to launch the job only once.

    launcher.run(job, new JobParameters());

The job is unique identified by its id together with the parameters. Currently there is no way to make a distinction based on the parameters. Instead of adding new JobParameters() use the JobParamtersBuilderBuilder and add the current date and time.

JobParametersBuilder builder = new JobParametersBuilder();
builder.addDate("date", new Date());
launcher.run(job, builder.toJobParameters());

This will allow you to run the job multiple times.

Directrix answered 17/3, 2014 at 13:42 Comment(1)
is there an xml equivalent to this?Inefficacious
R
10

add the line to jobBuilderFactory chain

.incrementer(new RunIdIncrementer())

next (as you run job manually with jobLauncher and with custom JobParameters)

paramsBuilder.getNextJobParameters(job);

instead of .addDate("date", new Date()); like they advicing you

Ronel answered 17/11, 2019 at 15:1 Comment(2)
I use the incrementer and the switch -next when launching with CommandLineJobRunner, but occasionally I get this exception and I have to force the run by adding a dummy parameter to the command line, i.e. dummyPar=$(date).Glamorize
i got the error: "java.lang.IllegalStateException: A JobExplorer is required to get next job parameters". so it is necessary to have a JobExplorer in your JobConfiguration.Setup
I
4

The main logic is to ensure relevant uniqueness. You can use any parameter if the parameter value will change in every request...Shortly, you can use date or long or another type...

JobParameters jobParameters = new JobParametersBuilder()
                .addString("YOUR_PARAM1", "VALUE-1")
                .addString("YOUR_PARAM2", "VALUE-2")
                .addLong("time",System.currentTimeMillis())
                .toJobParameters();

The following code block will provide the uniqueness for your request.

.addLong("time",System.currentTimeMillis())
In answered 16/8, 2021 at 7:39 Comment(0)
P
3

Yon can also include the time to make it more unique as I was getting the same error and passing the same JobParameters in my testing.

JobParameters jobParameters = new JobParametersBuilder()
                .addDate("date", new Date())
                .addLong("time",System.currentTimeMillis()).toJobParameters();
Pratincole answered 21/7, 2018 at 19:36 Comment(0)
Z
-3

In Application.property file Add new fields missing.

spring.batch.initialize-schema=always spring.batch.job.enabled = false

Zischke answered 21/6, 2019 at 10:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.