Initialize datasource bean on condition - spring

So, I need to make an initialization of DataSource by condition. I take all the db-config data from the .env file, where there is a special variable spring_profiles_active. When this variable is equal to "p2" I need to initialize secondDataSource, otherwise not. How do I do this?
My configuration:
#Configuration
public class JpaConfig {
#Value("${jdbc.url}")
private String jdbcUrl;
#Value("${jdbc.username}")
private String jdbcUsername;
#Value("${jdbc.password}")
private String jdbcPassword;
#Value("${jdbc.driverClassName}")
private String jdbcDriverClassName;
#Value("${second.jdbc.url}")
private String secondJdbcUrl;
#Value("${second.jdbc.username}")
private String secondJdbcUsername;
#Value("${second.jdbc.password}")
private String secondJdbcPassword;
#Value("${second.jdbc.driverClassName}")
private String secondJdbcDriverClassName;
#Bean
#Primary
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(jdbcDriverClassName);
dataSource.setUrl(jdbcUrl);
dataSource.setUsername(jdbcUsername);
dataSource.setPassword(EncryptionUtil.decryptProperty(jdbcPassword));
return dataSource;
}
#Bean
#Qualifier("secondDataSource")
public DataSource secondDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(secondJdbcDriverClassName);
dataSource.setUrl(secondJdbcUrl);
dataSource.setUsername(secondJdbcUsername);
dataSource.setPassword(EncryptionUtil.decryptProperty(secondJdbcPassword));
return dataSource;
}
#Bean
#Primary
public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(dataSource()); }
#Bean
#Qualifier("secondJdbcTemplate")
public JdbcTemplate secondJdbcTemplate(#Qualifier("secondDataSource") DataSource secondDataSource) {
return new JdbcTemplate(secondDataSource);
}
}
update:
I try to do this with #Condition annotation, but get an exception java.lang.NullPointerException: null in return activeProfile.equals("p2"). code:
#Configuration
#Conditional(SecondJpaConfigCondition.class)
public class SecondJpaConfig {
#Value("${second.jdbc.url}")
private String secondJdbcUrl;
#Value("${second.jdbc.username}")
private String secondJdbcUsername;
#Value("${second.jdbc.password}")
private String secondJdbcPassword;
#Value("${second.jdbc.driverClassName}")
private String secondJdbcDriverClassName;
#Bean
#Qualifier("secondDataSource")
public DataSource secondDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(secondJdbcDriverClassName);
dataSource.setUrl(secondJdbcUrl);
dataSource.setUsername(secondJdbcUsername);
dataSource.setPassword(EncryptionUtil.decryptProperty(secondJdbcPassword));
return dataSource;
}
#Bean
#Qualifier("secondJdbcTemplate")
public JdbcTemplate secondJdbcTemplate(#Qualifier("secondDataSource") DataSource secondDataSource) {
return new JdbcTemplate(secondDataSource);
}
}
#Component
public class SecondJpaConfigCondition implements Condition {
#Value("${spring.profiles.active}")
private String activeProfile;
#Override
public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {
return activeProfile.equals("p2");
}
}

I recommend you to use the #ConditionalOnProperty annotation. It can be used on the class or a method with the #Bean annotation. I would separate the configuration of the two databases in two different classes (it's not mandatory, you could us the annotion over the secondDataSource and secondJdbcTemplate methods) and do something like this:
JpaConfig (First Datasource):
#Configuration
public class JpaConfig {
#Value("${jdbc.url}")
private String jdbcUrl;
#Value("${jdbc.username}")
private String jdbcUsername;
#Value("${jdbc.password}")
private String jdbcPassword;
#Value("${jdbc.driverClassName}")
private String jdbcDriverClassName;
#Bean
#Primary
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(jdbcDriverClassName);
dataSource.setUrl(jdbcUrl);
dataSource.setUsername(jdbcUsername);
dataSource.setPassword(EncryptionUtil.decryptProperty(jdbcPassword));
return dataSource;
}
#Bean
#Primary
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}
JpaConditionalConfig (Second Datasource):
#Configuration
#ConditionalOnProperty(prefix = "cond", name = "spring_profiles_active", havingValue = "p2")
public class JpaOptionalConfig {
#Value("${second.jdbc.url}")
private String secondJdbcUrl;
#Value("${second.jdbc.username}")
private String secondJdbcUsername;
#Value("${second.jdbc.password}")
private String secondJdbcPassword;
#Value("${second.jdbc.driverClassName}")
private String secondJdbcDriverClassName;
#Bean
#Qualifier("secondDataSource")
public DataSource secondDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(secondJdbcDriverClassName);
dataSource.setUrl(secondJdbcUrl);
dataSource.setUsername(secondJdbcUsername);
dataSource.setPassword(EncryptionUtil.decryptProperty(secondJdbcPassword));
return dataSource;
}
#Bean
#Qualifier("secondJdbcTemplate")
public JdbcTemplate secondJdbcTemplate(#Qualifier("secondDataSource") DataSource secondDataSource) {
return new JdbcTemplate(secondDataSource);
}
}
Properties file where is defined the spring_profiles_active property, which will be read by Spring to know if the conditional beans should be created:
jdbc.url=jdbc:h2:mem:mydb
jdbc.username=sa
jdbc.password=password
jdbc.driverClassName=org.h2.Driver
second.jdbc.url=jdbc:h2:mem:mydb
second.jdbc.username=sa
second.jdbc.password=password
second.jdbc.driverClassName=org.h2.Driver
#THIS IS THE PROPERTY USED IN THE CONDITIONAL BEANS
conditional.spring_profiles_active=p2

Related

unable run Quartz JDBCJobStore with AbstractRoutingDataSource

I have implemented the application using Spring RoutingDataSource.
Spring -> DS1,
DS2
Based on the logged in URL I am changing the Data Source. it is working fine.
Coming to the quartz, I am unable to change the data source dynamically. Always jobs are getting scheduled on default data source.
#Configuration
public class SchedulerConfig {
#Autowired
private DataSource dataSource;
#Autowired
private QuartzProperties quartzProperties;
#Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
#Bean
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) {
Properties properties = new Properties();
properties.putAll(quartzProperties.getProperties());
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setJobFactory(jobFactory);
factory.setDataSource(dataSource);
factory.setGlobalJobListeners(jobListener());
factory.setQuartzProperties(properties);
return factory;
}
#Bean
public JobListenerSupport jobListener() {
return new JobListener();
}
}
Data Source Routing Configuration::
#Component
public class DataSourceRouting extends AbstractRoutingDataSource {
private DataSourceOneConfig dataSourceOneConfig;
private DataSourceTwoConfig dataSourceTwoConfig;
private DataSourceContextHolder dataSourceContextHolder;
public DataSourceRouting(DataSourceContextHolder dataSourceContextHolder, DataSourceOneConfig dataSourceOneConfig,
DataSourceTwoConfig dataSourceTwoConfig) {
this.dataSourceOneConfig = dataSourceOneConfig;
this.dataSourceTwoConfig = dataSourceTwoConfig;
this.dataSourceContextHolder = dataSourceContextHolder;
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put(DataSourceEnum.tenant1, dataSourceOneDataSource());
dataSourceMap.put(DataSourceEnum.tenant2, dataSourceTwoDataSource());
this.setTargetDataSources(dataSourceMap);
this.setDefaultTargetDataSource(dataSourceTwoDataSource());
}
#Override
protected Object determineCurrentLookupKey() {
return dataSourceContextHolder.getBranchContext();
}
public DataSource dataSourceOneDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(dataSourceOneConfig.getUrl());
dataSource.setUsername(dataSourceOneConfig.getUsername());
dataSource.setPassword(dataSourceOneConfig.getPassword());
return dataSource;
}
public DataSource dataSourceTwoDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(dataSourceTwoConfig.getUrl());
dataSource.setUsername(dataSourceTwoConfig.getUsername());
dataSource.setPassword(dataSourceTwoConfig.getPassword());
return dataSource;
}
}
Data Soruce Config ::
#RequiredArgsConstructor
#DependsOn("dataSourceRouting")
public class DataSourceConfig {
private final DataSourceRouting dataSourceRouting;
#Bean
#Primary
public DataSource dataSource() {
return dataSourceRouting;
}
#Bean(name = "entityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
return builder.dataSource(dataSource()).packages("com.model.entity").build();
}
#Bean(name = "transcationManager")
public JpaTransactionManager transactionManager(
#Autowired #Qualifier("entityManager") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
return new JpaTransactionManager(entityManagerFactoryBean.getObject());
}
}

Hikaricp configuration for multiple datasources

I have a multi database application. Users can select the database on the login page.
Then the database is routing selected database thanks for AbstractRoutingDataSource from Spring.
I want to use HikariCP, but it needs dataSourceUrl. But my Datasource URL changes dynamically. How can I configure Hikaricp for multiple databases?
File application.properties:
#database1 properties
app.database1.connection.url = url1
app.database1.connection.username = sameusername
app.database1.connection.password = samepassword
#database2 properties
app.database2.connection.url = url2
app.database2.connection.username = sameusername
app.database2.connection.password = samepassword
My Datasource configuration class example:
public class DataSourceConfiguration {
#Autowired(required = false)
private PersistenceUnitManager persistenceUnitManager;
#Bean
#ConfigurationProperties(prefix = "app.database1.connection")
public DataSource database1DataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "app.database2.connection")
public DataSource database2DataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#Primary
public DataSource appDataSource() {
DataSourceRouter router = new DataSourceRouter();
final HashMap<Object, Object> map = new HashMap<>(3);
map.put(DatabaseEnvironment.DATABASE1, database1DataSource());
map.put(DatabaseEnvironment.DATABASE2, database2DataSource());
router.setTargetDataSources(map);
return router;
}
#Bean
#Primary
#ConfigurationProperties("app.connection.jpa")
public JpaProperties appJpaProperties() {
return new JpaProperties();
}
private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(jpaProperties.isShowSql());
adapter.setDatabase(jpaProperties.getDatabase());
adapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
adapter.setGenerateDdl(jpaProperties.isGenerateDdl());
return adapter;
}
My session scoped class instead of context holder:
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PreferredDatabaseSession implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private DatabaseEnvironment preferredDb;
public DatabaseEnvironment getPreferredDb() {
return preferredDb;
}
public void setPreferredDb(DatabaseEnvironment preferredDb) {
this.preferredDb = preferredDb;
}
}
If I understand your requirement correctly, you intend to define two data sources and for a given request you want to route your queries to a particular data source based on some condition.
The solution is:
File application.properties
#database1 properties
app.database1.connection.url = url1
app.database1.connection.username = username1
app.database1.connection.password = password1
#database2 properties
app.database2.connection.url = url2
app.database2.connection.username = username2
app.database2.connection.password = password2
#default
default.datasource.key=dataSource1
File CommonRoutingDataSource.java
public class CommonRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceName();
}
public void initDataSources(final DataSource dataSource1, final DataSource dataSource2,
final String defaultDataSourceKey) {
final Map<Object, Object> dataSourceMap = new HashMap<Object, Object>();
dataSourceMap.put("dataSource1", dataSource1);
dataSourceMap.put("dataSource2", dataSource2);
this.setDefaultTargetDataSource(dataSourceMap.get(defaultDataSourceKey));
this.setTargetDataSources(dataSourceMap);
}
}
File DataSourceContextHolder.java
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
private DataSourceContextHolder() {
// Private no-op constructor
}
public static final void setDataSourceName(final String dataSourceName) {
Assert.notNull(dataSourceName, "dataSourceName cannot be null");
contextHolder.set(dataSourceName);
}
public static final String getDataSourceName() {
return contextHolder.get();
}
public static final void clearDataSourceName() {
contextHolder.remove();
}
}
File DataSourceConfig.java
public class DataSourceConfig {
#Autowired
private Environment env;
#Autowired
#Bean(name = "dataSource")
public DataSource getDataSource(final DataSource dataSource1, final DataSource dataSource2) {
final CommonRoutingDataSource dataSource = new CommonRoutingDataSource();
dataSource.initDataSources(dataSource1, dataSource2, env.getProperty("default.datasource.key"));
return dataSource;
}
#Bean(name = "dataSource1")
public DataSource getDataSource1() throws SQLException {
// The exact DataSource class imported shall be as per your requirement - HikariCP, or Tomcat etc.
final DataSource dataSource = new DataSource();
dataSource.setDriverClassName();
dataSource.setUrl(env.getProperty("app.database1.connection.url"));
// Set all data source attributes from the application.properties file
return dataSource;
}
#Bean(name = "dataSource2")
public DataSource getDataSource2() throws SQLException {
// The exact DataSource class imported shall be as per your requirement - HikariCP, or Tomcat etc.
final DataSource dataSource = new DataSource();
dataSource.setDriverClassName();
dataSource.setUrl(env.getProperty("app.database2.connection.url"));
// set all data source attributes from the application.properties file
return dataSource;
}
}
Now, somewhere in your code (either an Aspect or Controller), you need to dynamically set the data source conditionally:
DataSourceContextHolder.setDataSourceName("dataSource1");
Note: It's better to declare the data source names as enums rather than strings "dataSource1", "dataSource2", etc.
The below snippet works for me
first.datasource.jdbc-url=jdbc-url
first.datasource.username=username
first.datasource.password=password
.
.
.
.
=================== In Java Configuration File ==================
#Primary
#Bean(name = "firstDataSource")
#ConfigurationProperties(prefix = "first.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "firstEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("firstDataSource") DataSource dataSource) {
Map<String, String> props = new HashMap<String, String>();
props.put("spring.jpa.database-platform", "org.hibernate.dialect.Oracle12cDialect");
.
.
.
return builder.dataSource(dataSource).packages("com.first.entity").persistenceUnit("firstDB")
.properties(props)
.build();
}
#Primary
#Bean(name = "firstTransactionManager")
public PlatformTransactionManager firstTransactionManager(
#Qualifier("firstEntityManagerFactory") EntityManagerFactory firstEntityManagerFactory) {
return new JpaTransactionManager(firstEntityManagerFactory);
}
second.datasource.jdbc-url=jdbc-url
second.datasource.username=username
second.datasource.password=password
.
.
.
.
=================== In Java Configuration File ==================
#Bean(name = "secondDataSource")
#ConfigurationProperties(prefix = "second.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "secondEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("secondDataSource") DataSource dataSource) {
Map<String, String> props = new HashMap<String, String>();
props.put("spring.jpa.database-platform", "org.hibernate.dialect.Oracle12cDialect");
.
.
.
return builder.dataSource(dataSource).packages("com.second.entity").persistenceUnit("secondDB")
.properties(props)
.build();
}
#Bean(name = "secondTransactionManager")
public PlatformTransactionManager secondTransactionManager(
#Qualifier("secondEntityManagerFactory") EntityManagerFactory secondEntityManagerFactory) {
return new JpaTransactionManager(secondEntityManagerFactory);
}

Hibernate Transaction Advice in Spring MVC

My project is using Spring MVC4, Hibernate 5. I have configured hibernate transaction with Advice Interceptor, but it does not rollback as I would like. Please help me, what is the problem with my configuration?
All my code is as below:
1. Hibernate config:
#Configuration
#EnableTransactionManagement
public class DataSourceConfiguration {
#Autowired
private Environment env;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
new String[] {env.getProperty("spring.hibernate.packagesToScan")});
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
Spring Advice Interceptor:
#Aspect
#Configuration
public class TxAdviceInterceptor {
private static final String TX_METHOD_NAME = "*";
#Value(value = "${tx-advice.timeout:-1}")
private Integer txMethodTimeout = -1;
private static final String AOP_POINTCUT_EXPRESSION =
"execution(* com.ptg.service..*.*(..))";
#Autowired
private PlatformTransactionManager transactionManager;
#Bean
public TransactionInterceptor txAdvice() {
MatchAlwaysTransactionAttributeSource source = new MatchAlwaysTransactionAttributeSource();
RuleBasedTransactionAttribute transactionAttribute = new RuleBasedTransactionAttribute();
transactionAttribute.setName(TX_METHOD_NAME);
transactionAttribute.setRollbackRules(
Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
transactionAttribute.setTimeout(txMethodTimeout);
source.setTransactionAttribute(transactionAttribute);
return new TransactionInterceptor(transactionManager, source);
}
#Bean
public Advisor txAdviceAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
return new DefaultPointcutAdvisor(pointcut, txAdvice());
}
}
DAO:
#Repository
public abstract class GenericDaoImpl{
#Autowired
private SessionFactory sessionFactory;
#Override
public void S save(S entity) {
sessionFactory.save(entity);
}
}
DaoImpl:
#Repository
public class TagDaoImpl extends GenericDaoImpl{
}
#Repository
public class PostDaoImpl extends GenericDaoImpl{
}
Service:
#Service
public class PostServiceImpl{
#Autowired
private PostDao postDao;
#Autowired
private TagDao tagDao;
public void merge(Post post){
tagDao.save();
postDao.save();
}
}
As code above, I would like if postDao.save is error, tagDao is also rollback.
I have found the problem. My configuration is not wrong.
The problem is "Only unchecked exceptions (that is, subclasses of java.lang.RuntimeException) are rollbacked by default. For the case, a checked exception is thrown, the transaction will be committed".
I have test my code with NullPointerException error, therefore Transaction is not rollbacked.
Refer: https://www.catalysts.cc/wissenswertes/spring-transactional-rollback-on-checked-exceptions/
have you tried #Transactional annotation?

don't understand ioc java behavior

I try use IoC without xml. But I don't understand why #Autowired workin in the first case, and doesn't work in second case:
I have 3 classes:
#Configuration
public class DataSourceBean{
#Bean
public DataSource dataSource(){
DataSource ds = new DataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://192.168.1.99:3306/somethink");
ds.setUsername("devusr");
ds.setPassword("root");
ds.setInitialSize(5);
ds.setMaxActive(10);
ds.setMaxIdle(5);
ds.setMinIdle(2);
return ds;
}
}
public class AbstractDao {
#Autowired
private DataSource dataSource;
#Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public AbstractDao(){
System.out.println("dataSource = " + dataSource);
}
}
and
#RestController
public class PageController {
#Autowired
private DataSource dataSource;
private AbstractDao dao;
#RequestMapping(value = "/test" , method = RequestMethod.GET)
public String homePage(){
// System.out.println("$$ dataSource = " + dataSource);
AbstractDao dao = new AbstractDao();
return "";
}
}
and in a PageControllers autowiring works properly, I see that it doesn't null. And when I create new AbstractDao autowired doesn't work and dataSourse == null . I try add some annotations to class AbstractDao, but it doesn't work. what am I doing wrong? and how I must do it properly? Thanks
In your PageController you have to inject AbstractDao. Autowiring does not work when instantiating Objects with new operator. Try this instead in your PageController:
#RestController
public class PageController {
#Autowired
private DataSource dataSource;
#Autowired
private AbstractDao dao;
#RequestMapping(value = "/test" , method = RequestMethod.GET)
public String homePage(){
// System.out.println("$$ dataSource = " + dataSource);
return "";
}
}

Cannot autowired beans when separate configuration classes

I have a JavaConfig configurated Spring Batch job. The main job configuration file is CrawlerJobConfiguration. Before now, I have all the configuration (infrastructure, autowired beans, etc) in this class and it works fine. So I decided to separate the job configuration from autowired beans and infracstruture beans configuration and create another 2 configuration classes Beans and MysqlInfrastructureConfiguration.
But now I am having problems to run my job. I'm receiving a NullPointerException when the application try to use autowired fields indicating that the autowired is not working.
I put a breakpoint in the methods that create autowired beans to make sure that they are being called and really are, so I cannot realize what can be the problem.
java.lang.NullPointerException: null
at br.com.alexpfx.supermarket.batch.tasklet.StartCrawlerTasklet.execute(StartCrawlerTasklet.java:27) ~[classes/:na]
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
Main job configuration class:
#Configuration
#EnableBatchProcessing
public class CrawlerJobConfiguration {
#Autowired
private InfrastructureConfiguration infrastructureConfiguration;
#Autowired
private StepBuilderFactory steps;
#Autowired
Environment environment;
#Bean
public Job job(JobBuilderFactory jobs) {
Job theJob = jobs.get("job").start(crawlerStep()).next(processProductStep()).build();
((AbstractJob) theJob).setRestartable(true);
return theJob;
}
#Bean
public Step crawlerStep() {
TaskletStep crawlerStep = steps.get("crawlerStep").tasklet(crawlerTasklet()).build();
crawlerStep.setAllowStartIfComplete(true);
return crawlerStep;
}
#Bean
public Step processProductStep() {
TaskletStep processProductStep = steps.get("processProductStep")
.<TransferObject, Product>chunk(10)
.reader(reader())
.processor(processor())
.writer(writer())
.build();
processProductStep.setAllowStartIfComplete(true);
return processProductStep;
}
private Tasklet crawlerTasklet() {
return new StartCrawlerTasklet();
}
private ItemProcessor<TransferObject, Product> processor() {
return new ProductProcessor();
}
private ItemReader<TransferObject> reader() {
return new ProductItemReader();
}
private ItemWriter<Product> writer() {
return new HibernateProductsItemWriter();
}
}
Beans configuration class:
#Configuration
#EnableBatchProcessing
public class Beans {
#Bean
public Crawler crawler() {
return new RibeiraoCrawler(new UserAgentFactory());
}
#Bean
public ProductBo productBo() {
return new ProductBoImpl();
}
#Bean
public ProductDao productDao() {
return new ProductDaoImpl();
}
#Bean
public CrawlerListener listener() {
CrawlerListener listener = new RibeiraoListener();
return listener;
}
#Bean
public ProductList getProductList() {
return new ProductList();
}
}
MysqlInfrastructureConfiguration:
#Configuration
#EnableBatchProcessing
#PropertySource("classpath:database.properties")
#EnableJpaRepositories(basePackages = {"br.com.alexpfx.supermarket.domain"})
public class MysqlInfrastructureConfiguration implements InfrastructureConfiguration {
#Value("${jdbc.url}")
String url;
#Value("${jdbc.driverClassName}")
String driverClassName;
#Value("${jdbc.username}")
String username;
#Value("${jdbc.password}")
String password;
#Bean
#Override
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
#Bean
#Override
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory());
transactionManager.setDataSource(getDataSource());
return transactionManager;
}
#Bean
#Override
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(getDataSource());
em.setPackagesToScan(new String[]{"br.com.alexpfx.supermarket.domain"});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalJpaProperties());
em.afterPropertiesSet();
return em.getObject();
}
private Properties additionalJpaProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "create");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.setProperty("hibernate.show_sql", "true");
properties.setProperty("current_session_context_class", "thread");
return properties;
}
}
tasklet:
public class StartCrawlerTasklet implements Tasklet {
#Autowired
private Crawler crawler;
#Autowired
private CrawlerListener listener;
#Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
crawler.setListener(listener);
crawler.setStopCondition(new TimeLimitStopCondition(1, TimeUnit.MINUTES));
crawler.crawl();
return RepeatStatus.FINISHED;
}
}
StartCrawlerTasklet use the autowired annotation, so it should be a bean as well. So change your code :
private Tasklet crawlerTasklet() {
return new StartCrawlerTasklet();
}
to a bean definition:
#Bean
public Tasklet crawlerTasklet() {
return new StartCrawlerTasklet();
}

Resources