Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Batch - A job instance already exists: JobInstanceAlreadyCompleteException

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" />
like image 493
ruhungry Avatar asked Mar 17 '14 13:03

ruhungry


4 Answers

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

like image 102
Yura Avatar answered Oct 02 '22 17:10

Yura


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.

like image 31
M. Deinum Avatar answered Oct 02 '22 16:10

M. Deinum


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();
like image 26
Jeff Cook Avatar answered Oct 02 '22 17:10

Jeff Cook


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())
like image 43
bmck Avatar answered Oct 02 '22 15:10

bmck