Reputation: 109
I need to make a customized reader by extending JdbcCursorItemReader. I am doing it as below:
package sample.peektry;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("myReader")
public class MyReader implements ItemReader<MyBean> {
@Autowired
MyPeekableReader myPeekableReader;
public MyPeekableReader getMyPeekableReader() {
return myPeekableReader;
}
public void setMyPeekableReader(MyPeekableReader myPeekableReader) {
this.myPeekableReader = myPeekableReader;
}
@Override
public MyBean read() throws Exception, UnexpectedInputException,
ParseException, NonTransientResourceException {
System.out.println(" I will peek and read... :)");
return null;
}
}
package sample.peektry;
import org.springframework.batch.item.support.SingleItemPeekableItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("myPeekableReader")
public class MyPeekableReader extends SingleItemPeekableItemReader<MyBean> {
@Autowired
private MyJdbcReader myJdbcReader;
public MyJdbcReader getMyJdbcReader() {
return myJdbcReader;
}
public void setMyJdbcReader(MyJdbcReader myJdbcReader) {
this.myJdbcReader = myJdbcReader;
}
}
Further, MyRowMapper implements RowMapper
and has @Component("myRowMapper")
. Similarly, MyPrepStmntSetter implements PreparedStatementSetter
and has @Component("myPrepStmntSetter")
package sample.peektry;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("myJdbcReader")
public class MyJdbcReader extends JdbcCursorItemReader<MyBean> {
@Resource
DataSource dataSource;
@Autowired
MyRowMapper myRowMapper;
@Autowired
MyPrepSetter myPrepSetter;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public MyRowMapper getMyRowMapper() {
return myRowMapper;
}
public void setMyRowMapper(MyRowMapper myRowMapper) {
this.myRowMapper = myRowMapper;
}
public MyPrepSetter getMyPrepSetter() {
return myPrepSetter;
}
public void setMyPrepSetter(MyPrepSetter myPrepSetter) {
this.myPrepSetter = myPrepSetter;
}
}
Configurations:
in batch-infra.xml:
<batch:job-repository id="jobRepository" data-source="dataSource"
transaction-manager="transactionManager" />
<!-- connect to database -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver" />
<property name="url" value="jdbc:derby://localhost:1527/MyDB" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository"></property>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
batch-jobs.xml:
<import resource="classpath:/META-INF/spring/batch/jobs/myJob.xml" />
in app-context.xml(after xmlns and all):
<context:component-scan base-package="sample.peektry" />
<import resource="classpath:/META-INF/spring/batch/batch-infra.xml" />
<import resource="classpath:/META-INF/spring/batch/batch-jobs.xml" />
The above configuration is working fine for all the properties except dataSource. I.e. on running, I am getting: IllegalArgumentException DataSource must be provided
Stacktrace:
2014-02-16 14:56:07,195 INFO [org.springframework.context.support.ClassPathXmlApplicationContext] - <Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@c7e553: startup date [Sun Feb 16 14:56:07 IST 2014]; root of context hierarchy>
2014-02-16 14:56:07,291 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - <Loading XML bean definitions from class path resource [META-INF/spring/app-context.xml]>
2014-02-16 14:56:07,625 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - <Loading XML bean definitions from class path resource [META-INF/spring/batch/batch-infra.xml]>
2014-02-16 14:56:10,210 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - <Loading XML bean definitions from class path resource [META-INF/spring/batch/batch-jobs.xml]>
2014-02-16 14:56:12,766 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - <Loading XML bean definitions from class path resource [META-INF/spring/batch/jobs/myJob.xml]>
2014-02-16 14:56:12,872 INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - <Overriding bean definition for bean 'myJob': replacing [Generic bean: class [org.springframework.batch.core.configuration.xml.SimpleFlowFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] with [Generic bean: class [org.springframework.batch.core.configuration.xml.JobParserJobFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null]>
2014-02-16 14:56:13,115 INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - <Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1d332b: defining beans [myJdbcReader,myPeekableReader,myPrepSetter,myReader,myRowMapper,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.batch.core.scope.internalStepScope,org.springframework.beans.factory.config.CustomEditorConfigurer,org.springframework.batch.core.configuration.xml.CoreNamespacePostProcessor,jobRepository,dataSource,jobLauncher,transactionManager,step1,myJob]; root of factory hierarchy>
2014-02-16 14:56:13,215 INFO [org.springframework.jdbc.datasource.DriverManagerDataSource] - <Loaded JDBC driver: org.apache.derby.jdbc.ClientDriver>
2014-02-16 14:56:13,304 INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - <Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1d332b: defining beans [myJdbcReader,myPeekableReader,myPrepSetter,myReader,myRowMapper,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.batch.core.scope.internalStepScope,org.springframework.beans.factory.config.CustomEditorConfigurer,org.springframework.batch.core.configuration.xml.CoreNamespacePostProcessor,jobRepository,dataSource,jobLauncher,transactionManager,step1,myJob]; root of factory hierarchy>
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myJdbcReader' defined in file [C:\Users\user\Documents\workspace-sts-3.3.0.RELEASE\sb-listener-test\target\classes\sample\peektry\MyJdbcReader.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: DataSource must be provided
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1420)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:192)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at sample.peektry.MyMain.main(MyMain.java:16)
Caused by: java.lang.IllegalArgumentException: DataSource must be provided
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.batch.item.database.AbstractCursorItemReader.afterPropertiesSet(AbstractCursorItemReader.java:150)
at org.springframework.batch.item.database.JdbcCursorItemReader.afterPropertiesSet(JdbcCursorItemReader.java:107)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
... 12 more
Where I am getting wrong? Is it due to mixing XML configuration and component scan?
Upvotes: 1
Views: 14454
Reputation: 4623
The problem is with your dataSource
field in your MyJdbcReader
class. Actually, you're not overriding the dataSource
field of the superclass, simply because it is private (in the superclass). See source code on grepcode.
What you're doing is to declare a new package-private field, which is by no means connected to the dataSource
field in the superclass. See inheritance principles in Java for more details.
By looking to the source code (mentioned in the hyperlink above), you can see at line 150 that one expects the dataSource
field to be set. But in your case, it's not! Just because you're wiring your DataSource
to a field that has nothing to do with the dataSource
in the superclass.
What I'd suggest you is to override the setter method (which is public) and move the annotation to the setter. Something like this:
Remove these lines:
@Resource
DataSource dataSource;
and change the setDataSource
definition like the following:
@Override
@Resource
public void setDataSource(DataSource dataSource){
super.setDataSource(dataSource);
}
and remove the getDataSource
definition. It will fail, as you won't have a dataSource
variable in scope anymore.
Upvotes: 2