Spring boot / Spring security with dedicated database for security - java

I am looking desesperatly for spring boot running with spring security configured with two different databases. One for application's data and the other one for spring security only.
I have worked with https://www.baeldung.com/spring-data-jpa-multiple-databases for multiple databases configurations.With that too https://www.laulem.com/dev/spring-boot/spring-security.html for spring security.
But after several work, I'am unable to have a working project with spring boot and spring security with two separates database.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#PropertySource({"classpath:application.properties"})
#EnableJpaRepositories(
basePackages = "com.myApp.security.repositories",
entityManagerFactoryRef = "productEntityManager",
transactionManagerRef = "productTransactionManager"
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/login").successHandler( myAuthenticationSuccessHandler() ).failureUrl("/login?error=true").permitAll()
.and().logout().deleteCookies("JSESSIONID").logoutUrl("/logout").logoutSuccessUrl("/login");
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(myAppPasswordEncoder());
}
/**
* Cryptage des mots de passe
*
* #return
*/
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* No password encryption (dev only!!!)
*
* #return
*/
#Bean
public myAppPasswordEncoder myAppPasswordEncoder() {
return new myAppPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
return new myAppUrlAuthenticationSuccessHandler();
}
#Bean
#ConfigurationProperties(prefix="spring.productdb-datasource")
public DataSource productDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean productEntityManager() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(productDataSource());
em.setPackagesToScan("com.myApp.security.metier.impl");
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
final HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
#Bean
public PlatformTransactionManager productTransactionManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(productEntityManager().getObject());
return transactionManager;
}
}
I get this kind of errors
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'securityConfig':
Unsatisfied dependency expressed through field 'customUserDetailsService';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'userDetailsService':
Unsatisfied dependency expressed through field 'userService';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'userService':
Unsatisfied dependency expressed through field 'userRepository';
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'userRepository' defined in com.myApp.security.repositories.UserRepository defined in #EnableJpaRepositories declared on SecurityConfig:
Cannot create inner bean '(inner bean)#2239ae10' 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)#2239ae10':
Cannot resolve reference to bean 'productEntityManager' while setting constructor argument;
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'productEntityManager': Requested bean is currently in creation:
Is there an unresolvable circular reference?
Edit:
The problem seems to be caused by WebSecurityConfigurerAdapter inheritance. Some beans are instanciated by this class and create conflict with the dedicated datasource I try to configure for the "security" part of my spring-boot app.
I'm still searching...
Edit 2:
If anyboby's got a git URL to a working sample example...

Related

Spring Boot Defining A Bean

I have this funny error, I say it is funny because my project was working and it just crashed.
I believe I have implemented everything I was supposed to implement to achieve the multi data source.
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'quoteController' defined in file [C:\Users\S4\Desktop\S4Projects\wirk-devserv-intranet\source\back-end\target\classes\com\api\controllers\QuoteController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'quoteServiceImpl' defined in file [C:\Users\S4\Desktop\S4Projects\wirk-devserv-intranet\source\back-end\target\classes\com\api\services\implementation\QuoteServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.api.repository.customer.CustomerRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Description:
Parameter 1 of constructor in com.api.services.implementation.QuoteServiceImpl required a bean of type 'com.api.repository.customer.CustomerRepository' that could not be found.
Action:
Consider defining a bean of type 'com.api.repository.customer.CustomerRepository' in your configuration.
#Autowired
private final CustomerRepository customerRepository;
#Autowired
private final QuoteRepository quoteRepository;
#Component
public interface CustomerRepository extends JpaRepository<Customer,
Integer> {
}
Config File
#EnableJpaRepositories(
basePackageClasses = {
QuoteRepository.class,
CustomerConfig.class
},
entityManagerFactoryRef = "quoteEntityManager",
transactionManagerRef = "quoteTransactionManager")
public class QuoteConfig {
#Bean(name = "quoteEntityManager")
#Primary
public LocalContainerEntityManagerFactoryBean quoteEntityManager(EntityManagerFactoryBuilder builder, #Qualifier("quoteDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages(Quote.class)
.persistenceUnit("S4DevservIntranet")
.build();
}
#Primary
#Bean("quoteDataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource quoteDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean("quoteTransactionManager")
public PlatformTransactionManager quoteTransactionManager(#Qualifier("quoteEntityManager") LocalContainerEntityManagerFactoryBean quoteEntityManager) {
return new JpaTransactionManager(Objects.requireNonNull(quoteEntityManager.getObject()));
}
}
You don't need to use #Component annotation since you use the spring-data JpaRepository interface. but if you wouldn't you have to use #Repository annotation.
In this part you add CustomerConfig class instead CustomerRepository in the list of JpaRepositories
#EnableJpaRepositories(
basePackageClasses = {
QuoteRepository.class,
CustomerConfig.class
},
It must be:
#EnableJpaRepositories(
basePackageClasses = {
QuoteRepository.class,
CustomerRepository.class
},
Error Message tells it everything, seems you haven't created bean for CustomerRepository, so annotate the class with #Repository or #Component.

Unresolveable Circular reference error when configuring multiple datasources in a spring batch

I'm trying to configure two datasources in my spring batch application. One for batch metadata tables, and another for the business tables.
Snippet from my application.properties file:
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=
spring.batchdatasource.url=
spring.batchdatasource.username=
spring.batchdatasource.password=
spring.batchdatasource.driver-class-name=
My batch config file:
#Configuration
public class SpringBatchConfig extends DefaultBatchConfigurer{
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
// #Autowired
// private DataSource dataSource;
#Autowired
private PlatformTransactionManager transactionManager;
#Bean(name = "batchDatasource")
#ConfigurationProperties(prefix="spring.batchdatasource")
public DataSource batchDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "primaryDatasource")
#ConfigurationProperties(prefix="spring.datasource")
#Primary
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Override
public JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(batchDataSource());
// factory.setDataSource(dataSource);
factory.setTransactionManager(transactionManager);
factory.setTablePrefix("schema1"+ ".BATCH_");
factory.afterPropertiesSet();
return factory.getObject();
}
/* Job and step bean definitions here */
My main class is the one annotated with #EnableBatchProcessing
#SpringBootApplication
#EnableBatchProcessing
public class SpringBatchExample1Application {
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}
I'm getting this Requested bean is currently in creation: Is there an unresolvable circular reference? when trying to configure two datasources. It works fine when using a single datasource by autowiring(refer the commented out lines of code) instead of creating multiple beans.
Following is the exception snippet:
Error creating bean with name 'springBatchConfig': Unsatisfied dependency expressed through method 'setDataSource' parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'batchDatasource': Requested bean is currently in creation: Is there an unresolvable circular reference?
I looked up and found out this occurs when there's a dependency on a bean which is still not created or is being created. I just see it in the createJobRepository method where datasource is being plugged. I still face the error even if I don't have the createJobRepository method.
It seems like the requirement is for the datasource beans to be created before others. I tried using the #Order annotation, but no luck.
EDIT:
I tried the solution from #Mykhailo Skliar's Accepted answer below, and serparated the Datasource beans into a new Configuration class. Though it resolved the initial Unresolveble circular reference issue anymore, it led me to the following error:
Error creating bean with name 'springBatchConfig': Invocation of init method failed; nested exception is org.springframework.batch.core.configuration.BatchConfigurationException: java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.
Based on this answer, I changed my url property names as follows:
spring.datasource.jdbc-url=
spring.datasource.jdbc-url=
Though it solved the jdbcUrl error, it posed another issue:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Reference to database and/or server name in 'sample-sql-server.schema1.MY_TABLE_NAME' is not supported in this version of SQL Server.
Both my data sources are Azure SQL server instances.
I looked up and found it was not possible to use multiple Azure SQL databases years ago, but based on this answer it should not be the case anymore.
The issue is most probably because of
factory.setDataSource(batchDataSource());
You should use autowired bean here, instead of calling batchDataSource()
I would split SpringBatchConfig in two beans:
#Configuration
public class DataSourceConfig {
#Bean(name = "batchDatasource")
#ConfigurationProperties(prefix="spring.batchdatasource")
public DataSource batchDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "primaryDatasource")
#ConfigurationProperties(prefix="spring.datasource")
#Primary
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
#Configuration
public class SpringBatchConfig extends DefaultBatchConfigurer{
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
#Qualifier("batchDataSource")
#Autowired
private DataSource batchDataSource;
#Autowired
private PlatformTransactionManager transactionManager;
#Override
public JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(batchDataSource);
factory.setTransactionManager(transactionManager);
factory.setTablePrefix("schema1"+ ".BATCH_");
factory.afterPropertiesSet();
return factory.getObject();
}
}

Circular reference involving containing bean 'XXX' - consider declaring the factory method as static for independence from its containing instance

I am creating two projects globalDB and GlobalWeb using spring. I want to create multiple database connections in GlobalWeb, Jdbc properties are fetched from GlobalDB project dao layer at the time of GlobalWeb initiation. When i run the global web, Am getting the following exception.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'globalWebConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.kla.it.dao.Dao com.kla.it.global.conf.GlobalWebConfig.dao; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'daoImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.springframework.jdbc.core.JdbcTemplate com.kla.it.dao.impl.DaoImpl.template; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'template' defined in class path resource [com/kla/it/database/DatabaseConfig.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.sql.DataSource]: : Error creating bean with name 'partsDs' defined in com.kla.it.global.conf.GlobalWebConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.jdbc.datasource.DriverManagerDataSource]: Circular reference involving containing bean 'globalWebConfig' - consider declaring the factory method as static for independence from its containing instance. Factory method 'getPartsDataSource' threw exception; nested exception is java.lang.NullPointerException; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'partsDs' defined in com.kla.it.global.conf.GlobalWebConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.jdbc.datasource.DriverManagerDataSource]: Circular reference involving containing bean 'globalWebConfig' - consider declaring the factory method as static for independence from its containing instance. Factory method 'getPartsDataSource' threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:834)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:446)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:328)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4680)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5143)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:717)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:690)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:705)
GlobalDB configuration class
#Configuration
#ComponentScan(basePackages = "com.kla.it")
#PropertySource(value = {"classpath:database.properties"})
public class DatabaseConfig {
#Autowired
private Environment env;
// #Bean(name="ds")
public DataSource getDataSource(){
System.out.println("++++++++++++++++DATABASE++++++++++++++START++++++++++++");
System.out.println("Method to crate data source");
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("net.snowflake.client.jdbc.SnowflakeDriver");
ds.setUrl(env.getRequiredProperty("db.url"));
ds.setUsername(env.getRequiredProperty( "db.username"));
ds.setPassword(env.getRequiredProperty( "db.password"));
return ds;
}
#Bean(name="template")
public JdbcTemplate jdbcTemplate(DataSource ds) {
System.out.println("Jdbc template method called.");
JdbcTemplate template = new JdbcTemplate(ds);
template.setResultsMapCaseInsensitive(true);
System.out.println("++++++++++++++++DATABASE++++++++++++++END+++++++++++");
return template;
}
}
GlobalDB Dao Layer class
#Repository
#Qualifier("dao")
public class DaoImpl implements Dao{
#Autowired
JdbcTemplate template;
#Override
public Map<String, String> getConnectionsData(String appName, String env) {
List<Map<String, Object>> reportsList = new ArrayList<>();
Map<String, String> dataSourceMap = new HashMap<>();
String qry = "SELECT * FROM DB_CONFIG WHERE name = '"+appName+"' AND ev = '"+env+"'";
reportsList = template.queryForList(qry);
int count = 1;
for (Map<String, Object> map : reportsList) {
dataSourceMap.put(String.valueOf(map.get("prop")), String.valueOf(map.get("val")));
}
return dataSourceMap;
}
}
GlobalWeb Config class
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.kla.it")
#PropertySource(value = {"classpath:global.properties"})
public class GlobalWebConfig extends WebMvcConfigurerAdapter {
#Autowired(required=true)
Dao dao;
#Bean(name = "partsDs")
public DriverManagerDataSource getPartsDataSource(){
Map<String, String> dsMap = dao.getConnectionsData("parts", "dev");
DriverManagerDataSource partsDs= new DriverManagerDataSource();
partsDs.setDriverClassName(dsMap.get("driver"));
partsDs.setUrl(dsMap.get("url"));
partsDs.setUsername(dsMap.get("user"));
partsDs.setPassword(dsMap.get("password"));
return partsDs;
}
#Bean(name = "cosmosDs")
public DriverManagerDataSource getCosmosDataSource(){
Map<String, String> dsMap = dao.getConnectionsData("cosmos", "dev");
DriverManagerDataSource cosmosDs = new DriverManagerDataSource();
cosmosDs.setDriverClassName(dsMap.get("driver"));
cosmosDs.setUrl(dsMap.get("url"));
cosmosDs.setUsername(dsMap.get("user"));
cosmosDs.setPassword(dsMap.get("password"));
return cosmosDs;
}
#Bean(name = "itsDs")
public DataSource getITSDataSource(){
Map<String, String> dsMap = dao.getConnectionsData("ITSecurity", "dev");
DriverManagerDataSource itsDs= new DriverManagerDataSource();
itsDs.setDriverClassName(dsMap.get("driver"));
itsDs.setUrl(dsMap.get("url"));
itsDs.setUsername(dsMap.get("user"));
itsDs.setPassword(dsMap.get("password"));
return itsDs;
}
#Bean(name = "sfPartsDB")
public JdbcTemplate snowFlakeParts() {
System.out.println("Jdbc template method called.");
DataSource cosmosDs = getPartsDataSource();
JdbcTemplate sfPartsDB = new JdbcTemplate(cosmosDs);
sfPartsDB.setResultsMapCaseInsensitive(true);
return sfPartsDB;
}
#Bean(name = "sfCosmosDB")
public JdbcTemplate snowFlakeCosmos() {
System.out.println("Jdbc template method called.");
DataSource cosmosDs = getCosmosDataSource();
JdbcTemplate sfCosmosDB = new JdbcTemplate(cosmosDs);
sfCosmosDB.setResultsMapCaseInsensitive(true);
return sfCosmosDB;
}
//
#Bean(name = "oracleDB")
public JdbcTemplate oracleTemplate() {
DataSource itsDs = getITSDataSource();
JdbcTemplate oracleDB = new JdbcTemplate(itsDs);
oracleDB.setResultsMapCaseInsensitive(true);
return oracleDB;
}
}
Please try with below code. It works!!
1. #Qualifier to particular datasource reference(by name).
2. #Import Aggregate all the configuration classes.
#Configuration
#ComponentScan(basePackages = "com.kla.it")
#PropertySource(value = {"classpath:database.properties"})
public class DatabaseConfig {
#Autowired
private Environment env;
#Bean(name="ds")
public DataSource getDataSource(){
System.out.println("++++++++++++++++DATABASE++++++++++++++START++++++++++++");
System.out.println("Method to crate data source");
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("net.snowflake.client.jdbc.SnowflakeDriver");
ds.setUrl(env.getRequiredProperty("db.url"));
ds.setUsername(env.getRequiredProperty( "db.username"));
ds.setPassword(env.getRequiredProperty( "db.password"));
return ds;
}
#Bean(name="template")
public JdbcTemplate jdbcTemplate(#Qualifier("ds")DataSource ds) {
System.out.println("Jdbc template method called.");
JdbcTemplate template = new JdbcTemplate(ds);
template.setResultsMapCaseInsensitive(true);
System.out.println("++++++++++++++++DATABASE++++++++++++++END+++++++++++");
return template;
}
}
#Import(DatabaseConfig.class) /* add this line to aggrgate java configuration classes*/
#Configuration
#EnableWebMvc
#ComponentScanenter code here(basePackages = "com.kla.it")
#PropertySource(value = {"classpath:global.properties"})
public class GlobalWebConfig extends WebMvcConfigurerAdapter {
}

Spring JpaConfiguration give an exception on running server

I try to configure a JpaConfiguration to working with jetty and H2 (just for a test).
When I run the server I'm receiving an error. But I don't understand the reason.
I'm using spring-data-jpa.
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "my.package.model.repository")
#ComponentScan(basePackages = {"my.package.model.services"})
public class JpaConfiguration {
#Bean
public DataSource dataSource() throws SQLException {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
#Bean
public EntityManagerFactory entityManagerFactory() throws SQLException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("my.package.model.entity");
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
factory.setPersistenceUnitName("test");
return factory.getObject();
}
#Bean
public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
#Bean
public PlatformTransactionManager transactionManager() throws SQLException {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
return txManager;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
The error is :
2013-11-29 13:36:33.042:WARN::Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'memberService': Injection o
f autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private my.package.model.repository.MemberRepository my.package.model.services.MemberServiceImpl.memberRepository; nested exception is org.springframework.beans.f
actory.BeanCreationException: Error creating bean with name 'memberRepository': Cannot create inner bean '(inner bean)' of type [org.springframework.orm.jp
a.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Err
or creating bean with name '(inner bean)#1': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception i
s org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [my/package/config/JpaConfiguration.class]: No matching factory method found: factory bean 'jpaConfiguration'; factory method 'entityManagerFactory()'. Check tha
t a method with the specified name exists and that it is non-static.:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [my/package/config/JpaConfiguration.class]: No matching factory method found: factory bean 'jpaConfiguration'; factory method 'entityManagerFactory()'. Check that
a method with the specified name exists and that it is non-static.
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:536)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.ja
va:1055)
Thanks a lot for your help.
You can do it in a more simple way: you need a single method with LocalContainerEntityManagerFactoryBean as return type. Than, you can inject the EntityManager bean wherever you want without any other fuss.
public class JpaConfiguration {
#Bean
public DataSource dataSource() throws SQLException {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws SQLException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("my.package.model.entity");
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
factory.setPersistenceUnitName("test");
return factory;
}
//remainder configuration for trasnactions and so on
}
#Service
public class Service{
#PersistenceContext(unitName = "test")
private EntityManager em;
}
The problem is coming from my web.xml file.
configuration must content configuration class and not package :
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>my.package.WebConfiguration</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Importatn line is :
<param-value>my.package.WebConfiguration</param-value>
I wrote only
<param-value>my.package</param-value>

Adding beans breaks Spring Configuration

EDIT 1:
I'm currently calling this from a Main class like so:
public class Main
{
public static void main(String[] args)
{
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringAppConfig.class);
DataSource dSource = ctx.getBean(DataSource.class);
System.out.println(dSource.getClass().toString());
if (dSource instanceof Log4jdbcProxyDataSource)
{
Log4jdbcProxyDataSource log4jdbcProxyDataSource = (Log4jdbcProxyDataSource) dSource;
Object lf = log4jdbcProxyDataSource.getLogFormatter();
System.out.println(lf.getClass().toString());
}
System.exit(0);
}
}
Original:
Code follows after explanation:
I have a Spring application with a JavaConfig, call it the primary app, that imports another Spring JavaConfig class from a library. This imported JavaConfig is supposed to wrap any DataSource created in the primary app with an Aspect, which has an autowired LogDelegator.
As long as the primary app contains only a DataSource, everything works. But as soon as I add an EntityManager to the primary app, I get a nested IllegalArgumentException saying that the LogDelegator is null.
Primary App's Config:
#Configuration
#Import(MonitoringConfig.class)
public class SpringAppConfig
{
#Bean
public DataSource dataSource()
{
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.H2).build();
return db;
}
}
Imported Library Config:
#Configuration
#EnableSpringConfigured
public class MonitoringConfig extends WebMvcConfigurerAdapter
{
#Bean
public LogDelegator logDelegator()
{
return new LogDelegator();
}
#Bean
public ConfigurationAspect configurationAspect()
{
return Aspects.aspectOf(ConfigurationAspect.class);
}
}
The Aspect:
#Configurable
public aspect ConfigurationAspect
{
#Autowired
LogDelegator logDelegator;
Object around() : execution(public DataSource (#Configuration *).*(..)) {
Object ret = proceed();
if (ret instanceof DataSource) {
Log4jdbcProxyDataSource dataSource = new Log4jdbcProxyDataSource((DataSource) ret);
dataSource.setLogFormatter(logDelegator);
return dataSource;
} else {
return ret;
}
}
This code works great until I add the following,
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
{
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setDatabase(Database.H2);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setJpaVendorAdapter(vendorAdapter);
return factory;
}
#Bean
public EntityManager entityManager()
{
return entityManagerFactory().getObject().createEntityManager();
}
#Bean
public PlatformTransactionManager transactionManager()
{
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return jpaTransactionManager;
}
and then I get:
java.lang.reflect.InvocationTargetException at java.lang.Thread.run(Thread.java:722)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class SpringAppConfig: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [publ
ic org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean com.fl.sas.configurable.config.SpringAppConfig.entityManagerFactory()] threw exception; ne
sted exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class SpringAppConfig: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public
javax.sql.DataSource SpringAppConfig.dataSource()] threw exception; nested exception is java.lang.IllegalArgumentException: log4j
dbc: logDelegator cannot be null.
Can anyone help?
I needed to add
#Depends(value="configurationAspect") on dataSource()
Luca Basso Ricci answered the question. If he ever adds the answer, I'll give him the credit. :)

Categories

Resources