I have a class of GenerateKey which is for spring mvc boot. Since it needs to contact database and message queue, so I tried to wire it using xml
#RestController
public class GenerateKey {
final private DriverManagerDataSource dataSource;
final private AmqpTemplate rabbitMQTemplate;
final private String queueName;
public GenerateKey(DriverManagerDataSource dataSource,AmqpTemplate rabbitMQTemplate,String queueName){
this.dataSource=dataSource;
this.rabbitMQTemplate =rabbitMQTemplate;
this.queueName =queueName;
}
#RequestMapping("/key/generate")
public String generateKey(#RequestParam(value = "businesskey") String businesskey, #RequestParam(value = "customer") String customer, Model model) {
JdbcTemplate jdbctemplate = new JdbcTemplate(dataSource);
JSONObject obj = new JSONObject();
obj.put("businesskey", businesskey);
obj.put("customer", customer);
rabbitMQTemplate.convertAndSend(queueName, obj.toString());
System.out.println(businesskey+customer);
return "greeting" + businesskey + customer;
}
}
the xml configurations is as the following and using
#ImportResource( { "rabbit-listener-context.xml" } )
to import it
<bean id="request" class="com.testcom.keyservice.GenerateKey" c:dataSource-ref="dataSource" c:rabbitMQTemplate-ref="keyRequestTemplate" c:queueName="${keyRequestQueueName}"/>
but it complain "No default constructor found" as following:
2014-11-26 21:42:16.095 INFO 17400 --- [ main] utoConfigurationReportLoggingInitializer :
Error starting ApplicationContext. To display the auto-configuration report enabled debug logging (start with --debug)
2014-11-26 21:42:16.097 ERROR 17400 --- [ main] o.s.boot.SpringApplication : Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'generateKey' defined in file [/home/oracle/NetBeansProjects/rabbitmq-native/keyService/build/classes/main/com/testcom/keyservice/GenerateKey.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.testcom.keyservice.GenerateKey]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.testcom.keyservice.GenerateKey.<init>()
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1095)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1040)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:505)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:725)
Exception in thread "main" at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicatixt.java:482)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:109)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
Since you are using Spring Boot I assume you have also #ComponentScan active in your application. Because of that you create two instances of GenerateKey class - once because of #RestController annotation, second time in XML. I believe that's not what you want to achieve.
Get rid of XML declaration of request bean representing GeneratorKey class and use only Java based autowiring.
#RestController
public class GenerateKey {
private final DriverManagerDataSource dataSource;
private final AmqpTemplate rabbitMQTemplate;
private final String queueName;
#Autowired
public GenerateKey(#Qualifier("dataSource") DriverManagerDataSource dataSource, #Qualifier("keyRequestTemplate") AmqpTemplate rabbitMQTemplate, #Value("${keyRequestQueueName}") String queueName) {
this.dataSource=dataSource;
this.rabbitMQTemplate =rabbitMQTemplate;
this.queueName =queueName;
}
Related
I have a spring batch written using Spring boot. My batch only reads from MongoDB and prints the record.
I'm not using any SQL DB nor have any dependencies declared in project but While running it I'm getting below exception:
s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'batchConfigurer' defined in class path resource [org/springframework/boot/autoconfigure/batch/BatchConfigurerConfiguration$JdbcBatchConfiguration.class]: Unsatisfied dependency expressed through method 'batchConfigurer' parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-01 10:43:39.485 ERROR 15104 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 1 of method batchConfigurer in org.springframework.boot.autoconfigure.batch.BatchConfigurerConfiguration$JdbcBatchConfiguration required a bean of type 'javax.sql.DataSource' that could not be found.
- Bean method 'dataSource' not loaded because #ConditionalOnProperty (spring.datasource.jndi-name) did not find property 'jndi-name'
- Bean method 'dataSource' not loaded because #ConditionalOnClass did not find required class 'javax.transaction.TransactionManager'
Action:
Consider revisiting the conditions above or defining a bean of type 'javax.sql.DataSource' in your configuration.
In my pom.xml I've added below dependancies:
spring-boot-starter-batch
spring-boot-starter-data-mongodb
spring-boot-starter-test
spring-batch-test
Here's my batch configuration class:
#Configuration
#EnableBatchProcessing
public class BatchConfig {
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Autowired
private MongoTemplate mongoTemplate;
#Bean
public Job job() throws Exception {
return jobBuilderFactory.get("job1").flow(step1()).end().build();
}
#Bean
public Step step1() throws Exception {
return stepBuilderFactory.get("step1").<BasicDBObject, BasicDBObject>chunk(10).reader(reader())
.writer(writer()).build();
}
#Bean
public ItemReader<BasicDBObject> reader() {
MongoItemReader<BasicDBObject> reader = new MongoItemReader<BasicDBObject>();
reader.setTemplate(mongoTemplate);
reader.setCollection("test");
reader.setQuery("{'status':1}");
reader.setSort(new HashMap<String, Sort.Direction>() {
{
put("_id", Direction.ASC);
}
});
reader.setTargetType((Class<? extends BasicDBObject>) BasicDBObject.class);
return reader;
}
public ItemWriter<BasicDBObject> writer() {
MongoItemWriter<BasicDBObject> writer = new MongoItemWriter<BasicDBObject>();
return writer;
}
}
Here's is my launcher class:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
#SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class MyBatchApplication {
...
}
Spring Batch requires the use of a relational data store for the job repository. Spring Boot enforces that fact. In order to fix this, you'll need to add an in memory database or create your own BatchConfigurer that uses the Map based repository. For the record, the recommended approach is to add an in memory database (HSQLDB/H2/etc).
#SpringBootApplication
public class DataProcessorApplication {
public static void main(String[] args) throws UnknownHostException {
SpringApplication app = new SpringApplication(DataProcessorApplication.class);
app.run();
}
PostProcessor class
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class BeanRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
private static final Logger LOG = LoggerFactory.getLogger(BeanRegistryPostProcessor.class);
#Autowired
private DataConfigurationService dataConfigurationService;
#Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory factory)
throws BeansException {
// we don't want to touch existing beans
}
#Override
public void postProcessBeanDefinitionRegistry(final BeanDefinitionRegistry registry){
dataConfigurationService.something(); // service bean is null here
}
}
My Service class
#Service
public class DataConfigurationService implements ApplicationListener<ApplicationReadyEvent> {
private static final Logger LOG = LoggerFactory.getLogger(DataConfigurationService.class);
#Override
public void onApplicationEvent(final ApplicationReadyEvent e) {
LOG.debug("Loading active DataConfiguration instance...");
}
}
Exception
java.lang.NullPointerException: null
at dataprocessor.configmodels.processor.BeanRegistryPostProcessor.postProcessBeanDefinitionRegistry(BeanRegistryPostProcessor.java:40)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:685)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:523)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:736)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:369)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:313)
at dataprocessor.DataProcessorApplication.main(DataProcessorApplication.java:47)
2017-01-07 12:42:47.802 WARN 8880 --- [ main] ationConfigEmbeddedWebApplicationContext : Exception thrown from LifecycleProcessor on context close
java.lang.IllegalStateException: LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#7a138fc5: startup date [Sat Jan 07 12:42:46 CET 2017]; root of context hierarchy
at org.springframework.context.support.AbstractApplicationContext.getLifecycleProcessor(AbstractApplicationContext.java:416)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1004)
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:963)
at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:793)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:324)
at dataprocessor.DataProcessorApplication.main(DataProcessorApplication.java:47)
2017-01-07 12:42:47.803 ERROR 8880 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Destroy method on bean with name 'org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory' threw an exception
java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#7a138fc5: startup date [Sat Jan 07 12:42:46 CET 2017]; root of context hierarchy
at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:403)
at org.springframework.context.support.ApplicationListenerDetector.postProcessBeforeDestruction(ApplicationListenerDetector.java:97)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:233)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:578)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:554)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:951)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:523)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:958)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1035)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1011)
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:963)
at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:793)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:324)
at dataprocessor.DataProcessorApplication.main(DataProcessorApplication.java:47)
POM
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
Spring initializes context in next sequence:
First, based on application configuration and automatically detected classes(#Component, #Service etc) bean definitions will be created and registered in BeanDefinitionRegistry.
After that Spring will auto-detect beans which implement BeanFactoryPostProcessor in their bean definitions and apply them before any other beans get created. Since your BeanRegistryPostProcessor is implementation of BeanFactoryPostProcessor it will be applied on this step.
After that Spring will auto-detect all beans which implement BeanPostProcessor interface and will apply them to any beans subsequently created. One of this beans is AutowiredAnnotationBeanPostProcessor which processes #Autoware annotation. That means your service will be injected on this step.
As you see you're trying to use DataConfigurationService bean before it will be injected into BeanRegistryPostProcessor. For solve this problem you can implement ApplicationContextAware interface in BeanRegistryPostProcessor and then get instance of service directly from application context:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class BeanRegistryPostProcessor
implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware{
private ApplicationContext applicationContext;
...
#Override
public void postProcessBeanDefinitionRegistry(final BeanDefinitionRegistry registry){
DataConfigurationService service = applicationContext.getBean(DataConfigurationService.class);
service.something();
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
More details about so called Container Extension Points you can find in Spring documentation.
Here is my case:
I have two databases: one sybase and one mssql. I wish to access both of the database in a single service class. For example, I want to get some data from sybase, then I need do some update on mssql.
I have setup two datasources based on multiple samples found online, but Im unable to access my second database (sybase).
Here is my code:
pom.xml
spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp
# Database
# spring.datasource.jndi-name=jdbc/database1
spring.datasource.driver-class-name=net.sourceforge.jtds.jdbc.Driver
spring.datasource.url=jdbc:jtds:sqlserver://database1/db_aes
spring.datasource.username=user1
spring.datasource.password=password1
# Keep the connection alive if idle for a long time (needed in production)
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery = SELECT 1
# 2nd Database
spring.secondDatasource.driver-class-name=net.sourceforge.jtds.jdbc.Driver
spring.secondDatasource.url=jdbc:jtds:sybase://database2/aidcconfig
spring.secondDatasource.username=user2
spring.secondDatasource.password=password2
spring.secondDatasource.hibernate.dialect = org.hibernate.dialect.SybaseASE15Dialect
spring.secondDatasource.testWhileIdle = true
spring.secondDatasource.validationQuery = SELECT 1
# Show or not log for each sql query
spring.jpa.show-sql = false
# Hibernate ddl auto (create, create-drop, update, validate)
spring.jpa.hibernate.ddl-auto = validate
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.EJB3NamingStrategy
# Use spring.jpa.properties.* for Hibernate native properties (the prefix is
# stripped before adding them to the entity manager)
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.SQLServerDialect
com.ibm.websphere.persistence.ApplicationsExcludedFromJpaProcessing=*
fileUploadServiceImpl
#Component("fileUploadService")
#Transactional
public class FileUploadServiceImpl implements FileUploadService {
#Autowired
#Qualifier("dbAesJdbcTemplate")
JdbcTemplate dbAesJdbcTemplate;
#Autowired
#Qualifier("aidcconfigJdbcTemplate")
JdbcTemplate aidcconfigJdbcTemplate;
private int uploadId = 1;
private void testDB(){
String db = aidcconfigJdbcTemplate.queryForObject("select db_name()", String.class);
System.out.println("database name: " + db);
}
...
}
DbAesDataSource
package config.database;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "dbAesEntityManagerFactory",
transactionManagerRef = "dbAesTransactionManager",
basePackages = {"web.fileUpload.repo.db_aes.dao"}
)
public class DbAesDataSource {
#Primary
#Bean(name="dbAesDataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dbAesDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name="dbAesJdbcTemplate")
public JdbcTemplate dbAesJdbcTemplate(#Qualifier("dbAesDataSource") DataSource dbAesDataSource)
{
return new JdbcTemplate(dbAesDataSource);
}
#Bean(name="dbAesEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean dbAesEntityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("dbAesDataSource") DataSource dbAesDataSource) {
return builder
.dataSource(dbAesDataSource)
.packages("web.fileUpload.repo.db_aes.models")
.build();
}
#Bean(name = "dbAesTransactionManager")
public PlatformTransactionManager dbAesTransactionManager(
#Qualifier("dbAesEntityManagerFactory") EntityManagerFactory dbAesEntityManagerFactory) {
return new JpaTransactionManager(dbAesEntityManagerFactory);
}
}
AidcconfigDataSource
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "aidcconfigEntityManagerFactory",
transactionManagerRef = "aidcconfigTransactionManager",
basePackages = {"web.fileUpload.repo.aidcconfig.dao"}
)
public class AidcconfigDataSource {
#Bean(name="aidcconfigDataSource")
#ConfigurationProperties(prefix = "spring.secondDatasource")
public DataSource aidcconfigDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name="aidcconfigJdbcTemplate")
public JdbcTemplate aidcconfigJdbcTemplate(#Qualifier("aidcconfigDataSource") DataSource aidcconfigDataSource)
{
return new JdbcTemplate(aidcconfigDataSource);
}
#Bean(name="aidcconfigEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean aidcconfigEntityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("aidcconfigDataSource") DataSource aidcconfigDataSource) {
return builder
.dataSource(aidcconfigDataSource)
.packages("web.fileUpload.repo.aidcconfig.models")
.build();
}
#Bean(name = "aidcconfigTransactionManager")
public PlatformTransactionManager aidcconfigTransactionManager(
#Qualifier("aidcconfigEntityManagerFactory") EntityManagerFactory aidcconfigEntityManagerFactory) {
return new JpaTransactionManager(aidcconfigEntityManagerFactory);
}
}
Here is my error:
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:1.3.5.RELEASE:run (default-cli) on
project testUpload: An exception occurred while running. null: InvocationTargetException: Error creating bean with
name 'fileDownloadController': Injection of autowired dependencies failed; nested exception is org.springframework
.beans.factory.BeanCreationException: Could not autowire field: private web.fileUpload.services.FileUploadService w
eb.fileUpload.controller.FileDownloadController.fileUploadService; nested exception is org.springframework.beans.fa
ctory.BeanCreationException: Error creating bean with name 'fileUploadService': Injection of autowired dependencies
failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org
.springframework.jdbc.core.JdbcTemplate web.fileUpload.services.FileUploadServiceImpl.dbAesJdbcTemplate; nested exc
eption is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springfr
amework.jdbc.core.JdbcTemplate] found for dependency: expected at least 1 bean which qualifies as autowire candidat
e for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=tr
ue), #org.springframework.beans.factory.annotation.Qualifier(value=dbAesJdbcTemplate)} -> [Help 1]
If I removed the Qualifier in the FileUploadServiceImpl, then any jdbcTemplate will only connect to my Primary database which is db_aes. How can I access to my second datasource using jdbcTemplate?
Following are some of the references I used:
Spring Boot, Spring Data JPA with multiple DataSources
https://www.infoq.com/articles/Multiple-Databases-with-Spring-Boot
Multiple DataSource and JdbcTemplate in Spring Boot (> 1.1.0)
Trial#1
I noticed that it is unable to create the bean, and I placed some logger in the AidcconfigDataSource class. As a result, I didn't see my method is being executed. Thus, I assumed that the application is not reading my AidcconfigDataSource class.
I relocated the config folder as such, from java/config to java/web/config:
now I have another error:
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:1.3.5.RELEASE:run (default-cli) on
project testUpload: An exception occurred while running. null: InvocationTargetException: Error creating bean with
name 'dataSourceInitializerPostProcessor': Injection of autowired dependencies failed; nested exception is org.spr
ingframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.beans.facto
ry.BeanFactory org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.beanFactory; nested e
xception is org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'aidc
configDataSource' defined in class path resource [web/config/database/AidcconfigDataSource.class]: factory-bean ref
erence points back to the same bean definition -> [Help 1]
Trial#2
I have changed my bean name from aidcconfigDataSource to aidcconfigDS and same to the primary datasource. Plus I have added "spring.jpa.open_in_view = false" in my application.properties. However another error happens. How to do this the right way?
2016-11-03 09:28:16.118 ERROR 11412 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.servic
e() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exce
ption is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springf
ramework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: dbAesTransa
ctionManager,aidcconfigTransactionManager] with root cause
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.
transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: dbAesTransactionMana
ger,aidcconfigTransactionManager
I think Spring Boot is trying to instantiate 2 beans with the same name:
aidcconfigDataSource.
One is your configuration class AidcconfigDataSource.class and the other one is the bean:
#Bean(name="aidcconfigDataSource")
#ConfigurationProperties(prefix = "spring.secondDatasource")
public DataSource aidcconfigDataSource(){
return DataSourceBuilder.create().build();
}
I have a repository of username/passwords which I am trying to configure into spring web security using a customUserDetailsService. My base package is called 'showcase'.
I have a WebSecurityConfiguration as:
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource datasource;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(datasource)
.and()
.userDetailsService(customUserDetailsService);
// #formatter:on
}
I am trying to configure this datasource as follows:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {
"showcase"
})
public class DatabaseConfig {
//#Resource
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
private static final String PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
private static final String PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY = "hibernate.ejb.naming_strategy";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
#Bean
public DataSource dataSource(Environment env) {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("java.sql.DriverManager");
dataSource.setUrl("jdbc:mysql://localhost:3306/mutibodb");
dataSource.setUsername("db");
dataSource.setPassword("");
return dataSource;
}
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, Environment env) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("showcase");
Properties jpaProperties = new Properties();
jpaProperties.put(PROPERTY_NAME_HIBERNATE_DIALECT,"showcase.MySQLDialect");
jpaProperties.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO,"create-drop");
jpaProperties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY,"org.hibernate.cfg.ImprovedNamingStrategy");
jpaProperties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL,false);
jpaProperties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL,true);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
The SQLDialect configuration is as follows:
public class MySQLDialect extends MySQL5InnoDBDialect {
#Override
public String getDropSequenceString(String sequenceName) {
return "drop sequence if exists " + sequenceName;
}
#Override
public boolean dropConstraints() {
return false;
}
I get an error which primarily says that: Unable to build Hibernate SessionFactory.
I am new to Spring and must be missing something. I am not using any xml based config and wish to stick to only java config. I can share details of the rest of my code if required.
Update:
Here is my CustomUserDetailsService:
#Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
AppUserRepository repository;
#Autowired
private UserService userService;
private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);
#Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
logger.info("username:"+name);
AppUser u = repository.findByUserName(name);
if (u == null)
throw new UsernameNotFoundException("User details not found with this username: " + name);
String username = u.getUsername();
String password = u.getPassword();
List authList = new ArrayList();
authList.add(new SimpleGrantedAuthority("USER"));
User user = new User(username, password, authList);
return user;
}
The UserService mentioned above saves a user in the AppUserRepository. The AppUser is an entity.
The entire error is:
2014-12-28 12:02:59.555 INFO 5220 --- [ost-startStop-1] org.hibernate.Version : HHH000412: Hibernate Core {4.3.1.Final}
2014-12-28 12:02:59.557 INFO 5220 --- [ost-startStop-1] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found
2014-12-28 12:02:59.560 INFO 5220 --- [ost-startStop-1] org.hibernate.cfg.Environment : HHH000021: Bytecode provider name : javassist
2014-12-28 12:02:59.722 INFO 5220 --- [ost-startStop-1] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {4.0.4.Final}
2014-12-28 12:02:59.753 WARN 5220 --- [ost-startStop-1] o.h.e.jdbc.internal.JdbcServicesImpl : HHH000342: Could not obtain connection to query metadata : No suitable driver found for jdbc:mysql://localhost:3306/mutibodb
2014-12-28 12:02:59.760 INFO 5220 --- [ost-startStop-1] org.hibernate.dialect.Dialect : HHH000400: Using dialect: showcase.MySQLDialect
2014-12-28 12:02:59.767 INFO 5220 --- [ost-startStop-1] o.h.e.jdbc.internal.LobCreatorBuilder : HHH000422: Disabling contextual LOB creation as connection was null
2014-12-28 12:02:59.835 ERROR 5220 --- [cat-startStop-1] org.apache.catalina.core.ContainerBase : A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[]]
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:799)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
... 6 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.setFilterChainProxySecurityConfigurer(org.springframework.security.config.annotation.ObjectPostProcessor,java.util.List) throws java.lang.Exception; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webSecurityConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private showcase.CustomUserDetailsService showcase.WebSecurityConfiguration.customUserDetailsService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDetailsService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: showcase.AppUserRepository showcase.CustomUserDetailsService.repository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'appUserRepository': Cannot create inner bean '(inner bean)#165fa72a' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#165fa72a': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [showcase/DatabaseConfig.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
I had not focused on the hibernate errors before. I guess my hibernate config is wrong. But is there a way of doing this without injecting the datasource? When I did not have the datasource field in the securrity config and was only using auth.userDetailsService(customUserDetailsService), I was getting a No qualifying bean of type [javax.sql.DataSource] is defined error. Trying to fix that lead me to this path.
I am trying to take under control the context loading process. As I can read in Spring reference documentation I determined to use Phased interface provided by Spring.
When starting, the objects with the lowest phase start first, and when
stopping, the reverse order is followed. Therefore, an object that
implements SmartLifecycle and whose getPhase() method returns
Integer.MIN_VALUE would be among the first to start and the last to
stop. At the other end of the spectrum, a phase value of
Integer.MAX_VALUE would indicate that the object should be started
last and stopped first (likely because it depends on other processes
to be running). When considering the phase value, it’s also important
to know that the default phase for any "normal" Lifecycle object that
does not implement SmartLifecycle would be 0. Therefore, any negative
phase value would indicate that an object should start before those
standard components (and stop after them), and vice versa for any
positive phase value.
public interface Phased {
int getPhase();
}
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
Only I want load two controllers in phased after all others have been initialized.
To achieve this behaviour I have implemented the Phased Interface in both Spring Components.
#Service to be initialized at firstly.
#Service
public class BatchTaskController implements Phased{
#Autowired
private ActorService actorService;
#Autowired
private SessionFactory sessionFactory;
public BatchTaskController(){
this.batch();
}
public void batch(){
//TO DO...
}
#Override
public int getPhase() {
return 1;
}
}
#Controller to be initialized after batch task #Service.
#Controller
public class MainController implements Phased{
#Autowired
private ActorService actorService;
#Autowired
private SessionFactory sessionFactory;
#RequestMapping(value = {"/", "/index"})
public String showHome(HttpServletRequest request) {
//TO DO
return "index";
}
#Override
public int getPhase() {
return 2;
}
}
ActorService #Service not implement Phased Interface. Therefore I guess as I could read in the Spring Reference article exposed above that ActorService #Service is starting before than previous #Controllers because ActorService #Service phase valor was 0 by default.
But, when I deploy my app i get the next NullPointerException error.
04-04-2014 08:34:10,286 AM ERROR ContextLoader:331 - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'batchTaskController' defined in file [E:\Desarrollo\Spring\pjcomspringehcache\target\pjcomspringehcache-1.0-SNAPSHOT\WEB-INF\classes\com\pjcom\pjcomspringehcache\BatchTaskController.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.pjcom.pjcomspringehcache.BatchTaskController]: Constructor threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1076)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1021)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4738)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5158)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:726)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:702)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:697)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:579)
at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1744)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.uti
l.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:744)
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.pjcom.pjcomspringehcache.BatchTaskController]: Constructor threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:164)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:89)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1069)
... 26 more
Caused by: java.lang.NullPointerException
at com.pjcom.pjcomspringehcache.BatchTaskController.batch(BatchTaskController.java:32)
at com.pjcom.pjcomspringehcache.BatchTaskController.<init>(BatchTaskController.java:23)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:148)
... 28 more
Why?
SOLUTION
#Service to be initialized at firstly.
#Service
public class BatchTaskController implements Phased{
#Autowired
private ActorService actorService;
#Autowired
private SessionFactory sessionFactory;
#PostConstruct
public void batch(){
//TO DO...
}
#Override
public int getPhase() {
return 1;
}
}