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>
Spring Batch auto configuration is enabled by adding @EnableBatchProcessing (from Spring Batch) somewhere in your context. By default it executes all Jobs in the application context on startup (see JobLauncherCommandLineRunner for details). You can narrow down to a specific job or jobs by specifying spring. batch.
public JobBuilder get(java.lang.String name) Creates a job builder and initializes its job repository. Note that if the builder is used to create a @Bean definition then the name of the job and the bean name might be different. Parameters: name - the name of the job Returns: a job builder.
What is a "Job Repository" in Spring Batch? "JobRepository" is the mechanism in Spring Batch that makes all this persistence possible. It provides CRUD operations for JobLauncher, Job, and Step instantiations.
public interface JobLauncher. Simple interface for controlling jobs, including possible ad-hoc executions, based on different runtime identifiers. It is extremely important to note that this interface makes absolutely no guarantees about whether or not calls to it are executed synchronously or asynchronously.
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...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With