title: Spring Batch define conditional flow java-based using steps in @JobScope
body: <p>I have a problem defining a java-based job configuration in Spring Batch containing a <strong>conditional flow using steps in @JobScope</strong>, i.e. injecting a job parameter into the step at job execution time.</p>
  
  <p>My Setup:</p>
  
  <ul>
  <li>Spring Batch 3.0.4</li>
  <li>Spring Batch Admin 2.0.0.M1 (providing <em>jobRepository, transactionManager, jobScope, stepScope</em> beans during runtime)</li>
  </ul>
  
  <p>Assuming I have a simple job containing two steps step1 and step2, then defining and executing the job configuration without conditional flow is working:</p>
  
  <pre><code>@Configuration
  public class SimpleJobConfiguration {
  
  @Bean
  @JobScope
  Step1Tasklet step1Tasklet(@Value("#{jobParameters['condition']}") String condition) {
      Step1Tasklet tasklet = new Step1Tasklet(condition);
      return tasklet;
  }
  
  // reader, writer ommitted
  
  @Bean
  @JobScope
  public Step step1(@Value("#{jobParameters['condition']}") String condition) {
      TaskletStep step = stepBuilderFactory().get(STEP_NAME_1)
              .tasklet(step1Tasklet(condition))
              .build();
      return step;
  }
  
  @Bean
  @JobScope
  public Step step2() {
      TaskletStep step = stepBuilderFactory().get(STEP_NAME_2)
              .&lt;String, String&gt;chunk(10)
              .reader(reader(null))
              .writer(writer())
              .allowStartIfComplete(true)
              .build();
      return step;
  }
  
  @Bean
  public Job simpleJob() {
      Job job = jobBuilderFactory().get(JOB_NAME)
              .start(step1(null))
              .next(step2())
              .build();
      return job;
  }
  </code></pre>
  
  <ul>
  <li>When deploying the job configuration, Spring Batch is initializing my Job bean <strong>without any exceptions</strong>. It does not initialize my steps - which makes sense because they are in Scope "Job".</li>
  <li>When launching an execution for this job configuration, Spring Batch is initializing both steps, injecting the job parameters as expected.</li>
  <li>The job is executed as expected, first step1 then step2.</li>
  </ul>
  
  <p>Now I want to add a simple conditional logic for my Job:</p>
  
  <p><strong>"IF (step1.exitStatus == "OK") THEN execute step2 ELSE finish job"</strong></p>
  
  <p>To achieve that I have defined a decider bean (implements JobExecutionDecider) and modified my Job definition:</p>
  
  <pre><code>@Bean
  public SimpleStepDecider decider() {
      SimpleStepDecider decider = new SimpleStepDecider(); 
      return decider;
  }
  
  @Bean
  // DOES NOT WORK; NEEDS TO BE FIXED!
  public Job simpleJob() {
      Job job = jobBuilderFactory().get(JOB_NAME)
              .start(step1(null))
              .next(decider())
              .on("OK")
              .to(step2())
              .end()
              .build();
      return job;
  }
  </code></pre>
  
  <p>Using this I got the following exception during deployment(!) of the job configuration:</p>
  
  <pre><code>14:28:07,299 ERROR batch.local-startStop-1 context.ContextLoader:331 - Context initialization failed
  org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'simpleJob' defined in com.foo.bar.SimpleJobConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.batch.core.Job]: Factory method 'simpleJob' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.step1': Scope 'job' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for job scope
          at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
  ...
          at org.springframework.batch.core.configuration.support.GenericApplicationContextFactory$ResourceXmlApplicationContext.&lt;init&gt;(GenericApplicationContextFactory.java:161)
  ...
  Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.batch.core.Job]: Factory method 'simpleJob' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.step1': Scope 'job' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for job scope
          at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
          at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
          ... 39 more
  Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.step1': Scope 'job' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for job scope
          at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:352)
          at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
          at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
          at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
          at com.sun.proxy.$Proxy33.getName(Unknown Source)
          at org.springframework.batch.core.job.builder.FlowBuilder.createState(FlowBuilder.java:282)
          at org.springframework.batch.core.job.builder.FlowBuilder.doStart(FlowBuilder.java:265)
          at org.springframework.batch.core.job.builder.FlowBuilder.start(FlowBuilder.java:122)
          at org.springframework.batch.core.job.builder.JobFlowBuilder.&lt;init&gt;(JobFlowBuilder.java:39)
          at org.springframework.batch.core.job.builder.SimpleJobBuilder.next(SimpleJobBuilder.java:133)
          at com.foo.bar.SimpleJobConfiguration.simpleJob(SimpleJobConfiguration.java:145)
          ... 40 more
  Caused by: java.lang.IllegalStateException: No context holder available for job scope
          at org.springframework.batch.core.scope.JobScope.getContext(JobScope.java:153)
          at org.springframework.batch.core.scope.JobScope.get(JobScope.java:92)
          at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
          ... 60 more
  </code></pre>
  
  <p>I tried out:</p>
  
  <ul>
  <li>moving step beans to "normal" scope</li>
  <li>Using step execution listener instead of decider</li>
  </ul>
  
  <p>But no luck at all...</p>
  
  <p>Funny enough, i was able to define the same job using xml-based configuration, and it works as expected (decider is using value of job parameter 'condition' as exit status code; I tested both "OK" and "NOTOK" and they work as expected):</p>
  
  <pre><code>&lt;bean id="reader" class="com.foo.bar.MyReader" scope="job"&gt;
  &lt;/bean&gt;
  
  &lt;bean id="writer" class="com.foo.bar.MyWriter" /&gt;
  
  &lt;bean id="decider" class="com.foo.bar.SimpleStepDecider" /&gt;
  
  &lt;bean id="step1Tasklet" class="com.foo.bar.Step1Tasklet" scope="job"&gt;
      &lt;constructor-arg value="#{jobParameters['condition']}" /&gt;
  &lt;/bean&gt;
  
  &lt;batch:job id="simpleJob" restartable="true"&gt;
      &lt;batch:step id="step1" next="decision" allow-start-if-complete="true"&gt;
          &lt;batch:tasklet ref="step1Tasklet" /&gt;
      &lt;/batch:step&gt;
      &lt;batch:decision id="decision" decider="decider"&gt;
          &lt;batch:end on="NOTOK" /&gt;
          &lt;batch:next on="OK" to="step2"/&gt;
          &lt;batch:fail on="*"/&gt;
      &lt;/batch:decision&gt;
      &lt;batch:step id="step2" allow-start-if-complete="true"&gt;
          &lt;batch:tasklet allow-start-if-complete="true"&gt;
              &lt;batch:chunk reader="reader" writer="writer" commit-interval="1000" /&gt;
          &lt;/batch:tasklet&gt;
      &lt;/batch:step&gt;
  &lt;/batch:job&gt;
  </code></pre>
  
  <p>Can anyone give me a hint how I to get my java configuration working, i.e. step beans are instanciated during job execution?</p>