Java - Spring-Batch and Quartz and Tomcat - Returning cached instance of job bean
<p>I've been trying to setup a Spring-Batch job using Quartz, all running in a Tomcat container.  I've got everything working using a number of Posts from Stackoverflow and various articles.</p>

<p>The batch job is setup to run every minute using Quartz and the CronExpression.  </p>

<p>The issue now is that the actual Spring batch job only runs once.  It's a successful run as the logs show output from both classes :</p>

<pre><code>2016-03-09 11:20:00,047  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1   RunFirstBatch.run  111 : RunFirstBatch().run() - Starting.................................[]
2016-03-09 11:20:00,047  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1   RunFirstBatch.run  112 : RunFirstBatch().run() - CWD......................................[C:\Users\n0002501\AppData\Local\CI Eclipse for Java EE]

...removed for brevity...

2016-03-09 11:20:03,880  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1   FirstBatch.execute  113 : FirstBatch().execute().................................................[** First Batch Job is Executing! **]
2016-03-09 11:20:03,880  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1   FirstBatch.execute  114 : FirstBatch().execute() Step Contribution...............................[[StepContribution: read=0, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]]
2016-03-09 11:20:03,880  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1   FirstBatch.execute  115 : FirstBatch().execute() Chunk Context...................................[ChunkContext: attributes=[], complete=false, stepContext=SynchronizedAttributeAccessor: [], stepExecutionContext={batch.stepType=org.springframework.batch.core.step.tasklet.TaskletStep, batch.taskletType=com.lmig.batch.FirstBatch}, jobExecutionContext={}, jobParameters={}]

...removed for brevity...

2016-03-09 11:20:05,542  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1   SimpleJobLauncher.run  136 : Job: [FlowJob: [name=firstBatchJob]] completed with the following parameters: [{}] and the following status: [COMPLETED]
2016-03-09 11:20:05,542  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1   RunFirstBatch.run  126 : RunFirstBatch().run() - Exit Status..............................[COMPLETED]
2016-03-09 11:20:05,542  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-1   RunFirstBatch.run  145 : RunFirstBatch().run()............................................[finally]
</code></pre>

<p>The Problem is on all sub-sequent runs, Spring returns a cached instance of the batch job class and doesn't run it stating that it's in a completed status  : </p>

<pre><code>2016-03-09 11:21:00,008  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2   RunFirstBatch.run  111 : RunFirstBatch().run() - Starting.................................[]
2016-03-09 11:21:00,008  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2   RunFirstBatch.run  112 : RunFirstBatch().run() - CWD......................................[C:\Users\n0002501\AppData\Local\CI Eclipse for Java EE]

...removed for brevity...

...Returning cached instance of singleton bean 'firstBatchJob'

...removed for brevity...

2016-03-09 11:21:01,730  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2   SimpleStepHandler.shouldStart  217 : Step already complete or not restartable, so no action to execute: StepExecution: id=1, version=3, name=stepOne, status=COMPLETED, exitStatus=COMPLETED, readCount=0, filterCount=0, writeCount=0 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
2016-03-09 11:21:01,730 DEBUG org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2   SimpleFlow.resume  178 : Completed state=firstBatchJob.stepOne with status=COMPLETED
2016-03-09 11:21:01,730 DEBUG org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2   SimpleFlow.resume  164 : Handling state=firstBatchJob.end1
2016-03-09 11:21:01,730 DEBUG org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2   SimpleFlow.resume  178 : Completed state=firstBatchJob.end1 with status=COMPLETED
2016-03-09 11:21:01,730 DEBUG org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2   AbstractJob.execute  305 : Job execution complete: JobExecution: id=1, version=1, startTime=Wed Mar 09 11:21:00 EST 2016, endTime=null, lastUpdated=Wed Mar 09 11:21:00 EST 2016, status=COMPLETED, exitStatus=exitCode=COMPLETED;exitDescription=, job=[JobInstance: id=0, version=0, Job=[firstBatchJob]], jobParameters=[{}]

...removed for brevity...

2016-03-09 11:21:02,050  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2   SimpleJobLauncher.run  136 : Job: [FlowJob: [name=firstBatchJob]] completed with the following parameters: [{}] and the following status: [COMPLETED]
2016-03-09 11:21:02,050  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2   RunFirstBatch.run  126 : RunFirstBatch().run() - Exit Status..............................[COMPLETED]
2016-03-09 11:21:02,050  INFO org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-2   RunFirstBatch.run  145 : RunFirstBatch().run()............................................[finally]
</code></pre>

<p>Things I've tried : </p>

<ul>
<li>scope="prototype" </li>
<li>restartable="true"</li>
<li>implemented an ApplicationContextAware class to get the ApplicationContext.</li>
<li>implemented an JobExecutionListener class for beforeJob() and afterJob().</li>
</ul>

<p>Here are the details : </p>

<pre><code>&lt;bean id="batchApplicationContext" class="com.lmig.cm.rore.refmigrator.BatchApplicationContextProvider" scope="singleton"/&gt;    

&lt;bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"&gt;
    &lt;property name="transactionManager" ref="transactionManager"/&gt;
&lt;/bean&gt;

&lt;bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher" scope="prototype"&gt;
    &lt;property name="jobRepository" ref="jobRepository" /&gt;
&lt;/bean&gt;

&lt;bean id="firstBatch" class="com.lmig.batch.FirstBatch" scope="prototype"/&gt;
&lt;batch:step id="firstBatchStepOne"&gt;
    &lt;batch:tasklet ref="firstBatch"/&gt;
&lt;/batch:step&gt;
&lt;batch:job id="firstBatchJob" restartable="true"&gt;
    &lt;batch:step id="stepOne" parent="firstBatchStepOne"/&gt;
&lt;/batch:job&gt;

&lt;bean id="runFirstBatch" class="com.lmig.cm.rore.refmigrator.RunFirstBatch"&gt;
    &lt;property name="context" ref="batchApplicationContext" /&gt;
&lt;/bean&gt; 
</code></pre>

<p>RunFirstBatch class : </p>

<pre><code>import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;

public class RunFirstBatch {

    public static final String ID              = RunFirstBatch.class.getName ();
    private String             SHORT_NAME      = "RunFirstBatch()";
    @SuppressWarnings("unused")
    private String                  SYSTEM_IDENTITY = String.valueOf ( System.identityHashCode ( this ) );

    private     Log                 log = LogFactory.getLog(RunFirstBatch.class.getName());

    //private     ApplicationContext  context = null;
    private     BatchApplicationContextProvider context;

    /**
     * Default constructor.
     */
    public RunFirstBatch() {
        this.context = new BatchApplicationContextProvider();
    }

    public  void    run() { 

        try { 
            getLog().info ( SHORT_NAME + ".run() - Starting.................................[" + "]" );
            getLog().info ( SHORT_NAME + ".run() - CWD......................................[" + System.getProperty ( "user.dir" ) + "]" );

            //context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
            //this.context = new ClassPathXmlApplicationContext("/RoreWebConfiguration.xml");

            //String[] springConfig  = {"/first-batch.xml"};
            //context = new ClassPathXmlApplicationContext ( springConfig );

            if ( getContext() != null ) { 
                if ( getContext().getApplicationContext () != null ) { 
                    JobLauncher jobLauncher = (JobLauncher)getContext().getApplicationContext ().getBean("jobLauncher");
                    Job job = (Job) getContext().getApplicationContext ().getBean("firstBatchJob");
                    JobExecution execution = jobLauncher.run ( job, new JobParameters() );
                    //jobLauncher.run ( job, new JobParameters() );
                    getLog().info ( SHORT_NAME + ".run() - Exit Status..............................[" + execution.getStatus () + "]" );
                }
                else { 
                    getLog().info ( SHORT_NAME + ".run() - ApplicationContext is NULL...............[NULL]" );

                }
            }
            else { 
                getLog().info ( SHORT_NAME + ".run() - BatchApplicationContext is NULL..........[NULL]" );

            }

        }
        catch ( Exception ltheXcp ) { 
            getLog().error ( SHORT_NAME + ".run() - Exception ...............................[" + ltheXcp.getMessage () + "]" );
            getLog().error ( ltheXcp );

        }
        finally { 
            getLog().info ( SHORT_NAME + ".run()............................................[finally]" );
            //if (context != null) {
            //    context = null;
            //}

        }
    }

    public Log getLog() {
        return log;
    }

    public void setLog(Log log) {
        this.log = log;
    }

    public BatchApplicationContextProvider getContext() {
        return context;
    }

    public void setContext(BatchApplicationContextProvider context) {
        this.context = context;
    }


}
</code></pre>

<p>FirstBatch class : </p>

<pre><code>import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

import com.lmig.cm.rore.refmigrator.CMRoreReferenceMigrator;

public class FirstBatch implements Tasklet {

    public static final String ID              = FirstBatch.class.getName ();
    private String             SHORT_NAME      = "FirstBatch()";
    @SuppressWarnings("unused")
    private String              SYSTEM_IDENTITY = String.valueOf ( System.identityHashCode ( this ) );

    private     Log             log = LogFactory.getLog(FirstBatch.class.getName());

    public FirstBatch() {
        // TODO Auto-generated constructor stub
    }

    public FirstBatch( Log theLog ) {
        this.log = theLog;
    }

    /* (non-Javadoc)
     * @see org.springframework.batch.core.step.tasklet.Tasklet#execute(org.springframework.batch.core.StepContribution, org.springframework.batch.core.scope.context.ChunkContext)
     */
    @Override
    public RepeatStatus execute(StepContribution arg0, ChunkContext arg1)
        throws Exception {
        getLog().info(SHORT_NAME + ".execute().................................................[** First Batch Job is Executing! **]");
        getLog().info(SHORT_NAME + ".execute() Step Contribution...............................[" + arg0.toString () + "]");
        getLog().info(SHORT_NAME + ".execute() Chunk Context...................................[" + arg1.toString () + "]");
        return RepeatStatus.FINISHED;
        //return RepeatStatus.CONTINUABLE;

    }

    public Log getLog() {
        return log;
    }

    public void setLog(Log log) {
        this.log = log;
    }


}
</code></pre>

<p>I suspect it's something simple, but I can't seem to find the correct setting, attribute or option to cause it to start the job fresh again.</p>

<p>thx in advance,
adym</p>

<hr>

<p>The solution per Michael's answer : </p>

<ul>
<li>Spring Batch 3.0.2 </li>
<li>JDK 1.7-79 </li>
<li>Spring 3.2.14 </li>
<li>Quartz 2.2.2 </li>
<li>Tomcat 6.0.44 </li>
</ul>

<p>The solution was to simply provide at least 1 JobParameter in the JobParameters() that allows Spring-Batch to "uniquely" identify one JobInstance versus another.  I just used the current Date/Time as a String : </p>

<p>Add the following code in the RunFirstBatch.run() method : </p>

<pre><code>        SimpleDateFormat    theFormat = new SimpleDateFormat ( "yyyy-MM-dd-HH-mm-ss" );

        if ( getBatchContext ().getApplicationContext () != null ) { 
            if ( getBatchContext ().getApplicationContext () != null ) { 
                JobLauncher     jobLauncher = (JobLauncher)getBatchContext ().getApplicationContext ().getBean("jobLauncher");
                Job             job = (Job) getBatchContext ().getApplicationContext ().getBean("firstBatchJob");

                // JOBPARAMETERS : Build the job parameters...
                // 
                Date            theDate = new Date();
                // format the date as yyyy-MM-dd-HH-mm-ss 
                String          theJobId = theFormat.format ( theDate );
                // job parameter (single) from the formatted date...
                JobParameter    idParm = new JobParameter ( theJobId );
                // parameters container...used for the JobParameters
                // constructor...
                Map&lt;String,JobParameter&gt; mapParms = new HashMap&lt;String,JobParameter&gt; ();
                mapParms.put ( "1", idParm );

                JobParameters   theParms = new JobParameters (mapParms);

                getLog().info ( SHORT_NAME + ".run() - Job Id...................................[" + theJobId + "]" );


                JobExecution    execution = jobLauncher.run ( job, theParms );
                //execution.setExitStatus ( ExitStatus.UNKNOWN );
                //jobLauncher.run ( job, new JobParameters() );
                getLog().info ( SHORT_NAME + ".run() - Exit Status..............................[" + execution.getStatus () + "]" );
            }
            else { 
                getLog().info ( SHORT_NAME + ".run() - ApplicationContext is NULL...............[NULL]" );

            }
        }
        else { 
            getLog().info ( SHORT_NAME + ".run() - BatchApplicationContext is NULL..........[NULL]" );

        }
</code></pre>