Reputation: 43
I'm trying to implement sample batch application using JSR-352 API and Spring Batch 3.0.4 as implementation.
Batch job execution fails during initialization phase on error while creating bean with name 'batchPropertyPostProcessor':
Exception in thread "main" javax.batch.operations.JobStartException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'batchPropertyPostProcessor': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.batch.core.jsr.launch.support.BatchPropertyBeanPostProcessor.setBatchPropertyContext(org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext); nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [dataListingStepListener] for bean with name 'scopedTarget.dataListingStepListener' defined in null; nested exception is java.lang.ClassNotFoundException: dataListingStepListener
at org.springframework.batch.core.jsr.launch.JsrJobOperator.start(JsrJobOperator.java:637)
at x98.BatchRunner.main(BatchRunner.java:18)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'batchPropertyPostProcessor': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.batch.core.jsr.launch.support.BatchPropertyBeanPostProcessor.setBatchPropertyContext(org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext); nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [dataListingStepListener] for bean with name 'scopedTarget.dataListingStepListener' defined in null; nested exception is java.lang.ClassNotFoundException: dataListingStepListener
at org.springframework.batch.core.jsr.configuration.support.SpringAutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(SpringAutowiredAnnotationBeanPostProcessor.java:262)
at org.springframework.batch.core.jsr.configuration.support.JsrAutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(JsrAutowiredAnnotationBeanPostProcessor.java:30)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:232)
at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:618)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:467)
at org.springframework.batch.core.jsr.launch.JsrJobOperator.start(JsrJobOperator.java:635)
... 1 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.batch.core.jsr.launch.support.BatchPropertyBeanPostProcessor.setBatchPropertyContext(org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext); nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [dataListingStepListener] for bean with name 'scopedTarget.dataListingStepListener' defined in null; nested exception is java.lang.ClassNotFoundException: dataListingStepListener
at org.springframework.batch.core.jsr.configuration.support.SpringAutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(SpringAutowiredAnnotationBeanPostProcessor.java:575)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.batch.core.jsr.configuration.support.SpringAutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(SpringAutowiredAnnotationBeanPostProcessor.java:259)
... 13 more
Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [dataListingStepListener] for bean with name 'scopedTarget.dataListingStepListener' defined in null; nested exception is java.lang.ClassNotFoundException: dataListingStepListener
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1325)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:594)
at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:526)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:387)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:354)
at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:187)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1002)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:960)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
at org.springframework.batch.core.jsr.configuration.support.SpringAutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(SpringAutowiredAnnotationBeanPostProcessor.java:532)
... 15 more
Caused by: java.lang.ClassNotFoundException: dataListingStepListener
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.springframework.util.ClassUtils.forName(ClassUtils.java:247)
at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:395)
at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1346)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1317)
... 24 more
My batch runner:
public class BatchRunner {
public static void main(String[] args) {
System.setProperty("JSR-352-BASE-CONTEXT", "x98_batch_local.xml");
Properties jobParameters = new Properties();
jobParameters.put("message", "Hello!");
JobOperator jobOperator = BatchRuntime.getJobOperator();
jobOperator.start("x98SampleJob", jobParameters);
}
}
My sampleJob definition:
<?xml version="1.0" encoding="UTF-8"?>
<job id="x98SampleJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd"
version="1.0">
<step id="simple" next="dataListing">
<batchlet ref="simpleBatchlet">
<properties>
<property name="message" value="#{jobParameters['message']}" />
</properties>
</batchlet>
</step>
<step id="dataListing">
<listeners>
<listener ref="dataListingStepListener"/>
</listeners>
<chunk item-count="3">
<reader ref="dataListingItemReader" />
<processor ref="dataListingItemProcessor"/>
<writer ref="dataListingItemWriter"/>
</chunk>
</step>
</job>
My Spring bean configuration for reader, writer, processor and listener
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd">
<import resource="classpath*:x98_services.xml"/>
<bean name="simpleBatchlet" class="x98.batch.SimpleBatchlet"/>
<bean name="dataListingItemProcessor" class="x98.batch.DataListingItemProcessor"/>
<bean name="dataListingItemReader" class="x98.batch.DataListingItemReader">
<property name="tx98DatasService" ref="tx98DatasService"/>
<property name="tx98StructureService" ref="tx98StructureService"/>
</bean>
<bean name="dataListingItemWriter" class="x98.batch.DataListingItemWriter"/>
<bean name="dataListingStepListener" class="x98.batch.DataListingStepListener">
<property name="tx98StatusService" ref="tx98StatusService"/>
</bean>
</beans>
Reader, writer, and processor are instantiated by Spring and used properly in job run. When I added listener to job definition it stopped to work.
I debugged the code and I saw that dataListingStepListener bean was initialized in the Spring context. I don't understand why reference to listener (<listener ref="dataListingStepListener"/>
) is not recognized as Spring bean and Spring is trying instead load class named "dataListingStepListener".
Is my configuration OK? Am I doing something wrong?
Upvotes: 2
Views: 7516
Reputation: 21483
After reviewing the Gist, there are a couple notes:
Job specific beans must be in the child context
Spring Batch's JSR-352 context management works via a parent/child relationship. The parent context is defined by the baseContext.xml or can be overriden as you have done via the system property JSR-352-BASE-CONTEXT
. That context is intended to be used only for infrastructure beans and is not processed by the JSR-352 post processors for things like property injection. You said "When I added listener to job definition it stopped to work." If you looked closely, while the job ran, your property wasn't injected into SimpleBatchlet
(message was null
).
The reason for this is if you want to run multiple jobs, you shouldn't need to create multiple JobRepository
instances, multiple connection pools, etc. so we only bootstrap the parent context once. Since we only bootstrap it once, we don't have the values for things like job properties to inject at that time (and they may change from job to job).
So with that, all job specific beans should be in the child context.
Defining a child context for JSR-352
JSR-352 provides three different ways of handling bean creation:
ref
attribute of the artifact definition in the job XML and it will be created using a no-arg constructor.With that, we turn the control over to you as to how bean instances are created. By default, in Spring, beans are singletons that are created on startup. However, Spring Batch provides scopes (step
or job
) to allow for late binding of properties. If you want property injection, a bean must be defined as step
scope (SimpleBatchlet
was not which is why the message is null
the first run).
So how do you achieve the separation of concerns between the Spring DI and standards based JSR-352 when defining your job's context? Simple. Import the job XML into the Spring XML and pass the Spring XML name to the JobOperator
to be launched. In your case, instead of:
public static void main(String[] args) {
System.setProperty("JSR-352-BASE-CONTEXT", "x98_batch_local.xml");
Properties jobParameters = new Properties();
jobParameters.put("message", "Hello!");
JobOperator jobOperator = BatchRuntime.getJobOperator();
jobOperator.start("x98SampleJob", jobParameters);
}
Add the following to your x98_batch_local.xml:
<import resource="classpath*:x98SampleJob.xml"/>
And then execute your job via:
public static void main(String[] args) {
Properties jobParameters = new Properties();
jobParameters.put("message", "Hello!");
JobOperator jobOperator = BatchRuntime.getJobOperator();
jobOperator.start("x98_batch_local.xml", jobParameters);
}
The above tweak and setting your SimpleBatchlet
to be step scoped will cause your job to run as expected.
NOTE: As a result of this question, I've created issue BATCH-2388 to better document the context considerations discussed above.
Upvotes: 1