7

I am trying to inject job parameters into a custom ItemReader. I have reviewed all of the StackOverflow notes on the subject (example: How to get access to job parameters from ItemReader, in Spring Batch?), and I see this is a common pain point that is mostly unresolved. I am hoping that a spring guru (@Michael Minella anyone) will see this and have some insight.

I have got as far as determining that the jobparameters are available about one out of 10 runs, even with no code or configuration changes. This is a case of a random success rather than a random failure, so it's proving hard to track down.

I dug into the spring code with the debugger, and determined that when this fails, no bean of the name jobParameters is registered in Spring at the time that the injection is taking place.

I am using Spring 4.1.4 with spring-batch 3.0.2 and spring-data-jpa 1.7.1 and spring-data-commons 1.9.1, running in java 8.

Java class

@Component("sourceSelectionReader")
@Scope("step")
public class SourceSelectionReaderImpl  
implements ItemReader<MyThing> {
    private Map<String,Object> jobParameters;

// ... snip ...


    @Autowired
    @Lazy
    @Qualifier(value="#{jobParameters}")
    public void setJobParameters(Map<String, Object> jobParameters) {
        this.jobParameters = jobParameters;
    }
}

Job launch parameters:

launch-context.xml job1 jobid(long)=1

launch-context.xml (minus the fluff):

<context:property-placeholder location="classpath:batch.properties" />

<context:component-scan base-package="com.maxis.maximo.ilm" />

<jdbc:initialize-database data-source="myDataSource"  enabled="false">
    <jdbc:script location="${batch.schema.script}" />
</jdbc:initialize-database>

<batch:job-repository id="jobRepository" 
    data-source="myDataSource"
    transaction-manager="transactionManager"
    isolation-level-for-create="DEFAULT"
    max-varchar-length="1000"/>

<import resource="classpath:/META-INF/spring/module-context.xml" />

Module-context.xml (minus the fluff):

<description>Example job to get you started. It provides a skeleton for a typical batch application.</description>

<import resource="classpath:/META-INF/spring/hibernate-context.xml"/>
<import resource="classpath:/META-INF/spring/myapp-context.xml"/>

<context:component-scan base-package="com.me" />
<bean class="org.springframework.batch.core.scope.StepScope" />

<batch:job id="job1">
    <batch:step id="step0002"  >            
        <batch:tasklet transaction-manager="transactionManager" start-limit="100" >
            <batch:chunk reader="sourceSelectionReader" writer="selectedDataWriter" commit-interval="1" />
        </batch:tasklet>
    </batch:step>
</batch:job> 
Community
  • 1
  • 1
pojo-guy
  • 966
  • 1
  • 12
  • 39
  • how do you launch the job? – wassgren Jan 09 '15 at 14:31
  • And, why are you using `@Lazy` for the injection? Is that a needed for your task? – wassgren Jan 09 '15 at 14:37
  • I am launching the job from within my eclipse IDE, for now. It will be launched from command line when it goes live. I use @Lazy because it allows me to sidestep this problem and move on with the real project, pending a resolution. There are other ways to get the parameters into he bean but they will create support problems in the long run. – pojo-guy Jan 09 '15 at 17:29

2 Answers2

4

The important steps to get Job Parameters to work is to define the StepScope bean and to make sure that your reader is a @StepScope component.

I would try the following:

First make sure that there is a step-bean defined. This is nice to setup using Java Configuration:

@Configuration
public class JobFrameworkConfig {  
    @Bean
    public static StepScope scope() {
        return new StepScope();
    }
    // jobRegistry, transactionManager etc...
}

Then, make sure that your bean is step-scoped by the use of the @StepScope-annotation (almost as in your example). Inject a @Value that is not @Lazy.

@Component("sourceSelectionReader")
@StepScope // required, also works with @Scope("step")
public class SourceSelectionReaderImpl implements ItemReader<MyThing> {
    private final long myParam;

    // Not lazy, specified param name for the jobParameters
    @Autowired
    public SourceSelectionReaderImpl(@Value("#{jobParameters['myParam']}") final long myParam) {
        this.myParam = myParam;
    }

    // the rest of the reader...
}
wassgren
  • 18,651
  • 6
  • 63
  • 77
  • using @Scope("step") - the bean initializes but the step scoped job parameter is not available. using @StepScope, the bean fails to initialize: Cannot subclass final class class com.sun.proxy.$Proxy42 at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:446) .. etc – pojo-guy Jan 09 '15 at 17:43
  • @pojo-guy, weird failure. Please observe that there is a class called `StepScope` AND an annotation called `@StepScope`. Inconvenient ;) – wassgren Jan 09 '15 at 18:19
  • On another thread, accidental proxying with StepScope was discussed. Per @Mike Manella (Spring Batch tech lead) StepScope is a shortcut convenience for Scope("step", ...). The resolution for that is to understand and apply the Scope annotation with the correct parameters When I have a chance I will apply these and report the results. – pojo-guy Jan 10 '15 at 21:20
  • This resolves the double proxying, but there is still no jobParameters bean present to be wired when the reader class is being loaded. I either have a problem with mismatched contexts or I am missing something that is so expected that no one thinks of it.\ – pojo-guy Jan 13 '15 at 22:45
  • Is this possibly related to the many issues linked to https://jira.spring.io/browse/SPR-12111 ? – pojo-guy Jan 16 '15 at 23:29
  • The step scope bean can also be defined in xml as showin in the documentation using the following entry: http://docs.spring.io/spring-batch/trunk/reference/html/configureStep.html#step-scope – Samantha Catania Sep 14 '15 at 07:12
  • It's been a while since I laid this to rest. I had accidentally defined two context beans, so some pieces were being instantiated under one bean, and some under the other. I also had accidental proxying issues. The total resolution was to eliminate the extra context bean, and then to fully define the interface/implementation pairs for the obejcts that were being accidentally proxied. – pojo-guy Dec 03 '15 at 21:26
-1

Try add @DependsOn("jobParameters") after @Component("sourceSelectionReader")

Pavan
  • 337
  • 1
  • 4
  • 10