3

I have below spring batch job:

<job id="MyBatchJob" job-repository="jobRepository">

  <step id="ConfigurationReadStep">
        <tasklet ref="ConfigurationReadTasklet"  transaction-manager="jobRepository-transactionManager"/>
        <next on="COMPLETED"  to="NextStep" />  
  </step>        

  <step id="NextStep">
    <tasklet transaction-manager="jobRepository-transactionManager">
        <chunk reader="myItemReader" writer="myItemWriter" commit-interval="1000"/>
    </tasklet>
  </step>

     <listeners>
        <listener ref="jobListener" />
     </listeners>     

</job>

It has first step as a configuration read step where after some business logic, I come across with a list of queries. For e.g. say 10 queries. I know that I can promote this list using JobExecutionContext and PromotionListener.

However, I want to feed this queries 1 by 1 to my next step's reader as reader query and run that step in a loop till all the reader queries are consumed. I want to run each of this queries as a spring batch scenario as they may return huge number of items.

How can I do this ?

*********************************** Update **********************************

This is what I am trying to do:

<job id="MyJob" job-repository="jobRepository">

      <step id="ConfigurationReadStep">
            <tasklet ref="ConfigurationReadTasklet"  transaction-manager="jobRepository-transactionManager"/>
            <next on="COMPLETED"  to="MyNextStep" />    
             <listeners>
                <listener ref="promotionListener"/>
            </listeners>
      </step>        

    <step id="MyNextStep" next="limitDecision">
        <tasklet transaction-manager="jobRepository-transactionManager">
            <chunk reader="MyItemReader" writer="MyItemWriter" commit-interval="1000"/>
        </tasklet>
    </step>

    <decision id="limitDecision" decider="limitDecider">
        <next on="CONTINUE" to="MyNextStep" />
        <end on="COMPLETED" />
    </decision>

     <listeners>
        <listener ref="jobListener" />
     </listeners>

  </job>

<beans:bean id="jobListener" class="com.hsbc.gbm.dml.integration.batch.listener.SimpleJobListener" />

<beans:bean id="promotionListener" class="org.springframework.batch.core.listener.ExecutionContextPromotionListener">
    <beans:property name="keys" value="readerLimit,readerQueries,readerSQL" />
</beans:bean>

  <beans:bean id="ConfigurationReadTasklet" class="com.mypackage.ConfigurationReadTasklet" scope="step">
  </beans:bean>

<beans:bean id="MyItemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step">
    <beans:property name="dataSource" ref="myDataSource" />
    <beans:property name="sql" value="#{jobExecutionContext['readerSQL']}" />
    <beans:property name="rowMapper">
        <beans:bean  name="mapper" class="com.mypackage.MyRowMapper" />
    </beans:property>
</beans:bean>

<beans:bean id="MyItemWriter" class="com.mypackage.MyItemWriter" scope="step">
    <beans:constructor-arg ref="myDataSource" />
</beans:bean>

<beans:bean id="limitDecider" class="com.mypackage.StepLoopLimitDecider" scope="step"> 
    <beans:property name="readerQueries" value="#{jobExecutionContext['readerQueries']}" />
</beans:bean>     

In the tasklet, I will get list of reader queries. I will set readerQuery as first of these and go to next step which will run normally as a spring batch step.

Once this completes, I want my Decider to check if there are more reader queries, if yes, it will change readerQuery to next query and re-run NextStep, else the job will complete. Below is my Decider:

public class StepLoopLimitDecider implements JobExecutionDecider {

private List<String> readerQueries;

private int count = 1;

public FlowExecutionStatus decide(JobExecution jobExecution,StepExecution stepExecution) {
    if (count >= this.readerQueries.size()) {
        return new FlowExecutionStatus("COMPLETED");
    }
    else {
        System.out.println(count + ": "+readerQueries.get(count));
        jobExecution.getExecutionContext().put("readerSQL", readerQueries.get(count));
        count = count + 1;
        return new FlowExecutionStatus("CONTINUE");
    }
}

public void setReaderQueries(List<String> readerQueries) {
    this.readerQueries = readerQueries;
}   
}

However, this is not working. Step runs correctly the first time. But the Decider fails with below error:

    2016-12-06 14:46:24 ERROR AbstractJob:306 - Encountered fatal error executing job
org.springframework.batch.core.JobExecutionException: Flow execution ended unexpectedly
    at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:141)
    at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:281)
    at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:120)
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:48)
    at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:114)
    at org.springframework.batch.core.launch.support.CommandLineJobRunner.start(CommandLineJobRunner.java:348)
    at org.springframework.batch.core.launch.support.CommandLineJobRunner.main(CommandLineJobRunner.java:565)
Caused by: 
org.springframework.batch.core.job.flow.FlowExecutionException: Ended flow=MyBatchJob at state=MyBatchJob.limitDecision with exception
    at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:152)
    at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:124)
    at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:135)
    ... 6 more
Caused by: 
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.limitDecider': Scope 'step' 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 step scope
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:339)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
    at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:182)
    at $Proxy4.decide(Unknown Source)
    at org.springframework.batch.core.job.flow.support.state.DecisionState.handle(DecisionState.java:43)
    at org.springframework.batch.core.configuration.xml.SimpleFlowFactoryBean$DelegateState.handle(SimpleFlowFactoryBean.java:141)
    at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:144)
    ... 8 more
Caused by: 
java.lang.IllegalStateException: No context holder available for step scope
    at org.springframework.batch.core.scope.StepScope.getContext(StepScope.java:197)
    at org.springframework.batch.core.scope.StepScope.get(StepScope.java:139)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:325)
    ... 15 more

Can someone please help me with making this work.

cнŝdk
  • 31,391
  • 7
  • 56
  • 78
Vicky
  • 16,679
  • 54
  • 139
  • 232
  • A decision is not a step within Spring Batch so it cannot be step scoped. Make that job scoped and things should work from what I can see... – Michael Minella Dec 07 '16 at 15:34

1 Answers1

5

I suggest that you build your job dynamically based on results of your business logic which calculates the queries.

However, you cannnot do this with xml-configuration, therefore, I suggest that you use java-config api.

I have written several answers to similar questions on "how to create a job dynamically". Have a look at them and let me know, if you need furhter advise.

SpringBatch - javaconfig vs xml

Spring Batch Java Config: Skip step when exception and go to next steps

Spring batch repeat step ending up in never ending loop

Spring Batch - How to generate parallel steps based on params created in a previous step

Spring Batch - Looping a reader/processor/writer step

Community
  • 1
  • 1
Hansjoerg Wingeier
  • 4,274
  • 4
  • 17
  • 25
  • Thanks. Can you please check the update on my question. I am trying an approach but its not working. – Vicky Dec 06 '16 at 09:32
  • This will not work. SpringBatch is not intended to execute a specific step several times during a single job launch. The framework keeps several internal states of the step and its reader and writer. You would need to hack into the instantiated job structure during runtime and somehow reset the states of the step and the readers and writers. But that's so ugly and a dirty hack; definitely not something you wanna do. – Hansjoerg Wingeier Dec 06 '16 at 10:38
  • As I tried to explain, familiarize yourself with the Java-API. And then create your Job with Java-Code instead of XML. As far as I understood, after you launched the application, you can determine the different steps that have to be created. You can do this with the Java-Api. have a look at my answer here: http://stackoverflow.com/questions/37238813/spring-batch-looping-a-reader-processor-writer-step/37271735#37271735 – Hansjoerg Wingeier Dec 06 '16 at 10:41