Qualifier doesn't work for DataSource in Spring Boot - java

I have two datasources defined "datasource1" and "datasource2" (in xml configuration from a dependency).
Because of that I don't get JdbcTemplate configured by default so I need to do it manually, I do it like this:
1.
#Bean
public JdbcOperations jdbcOperations(DataSource datasource1) {
return new JdbcTemplate(datasource1);
}
2.
#Bean
public JdbcOperations jdbcOperations(#Qualifier("datasource1") DataSource datasource1) {
return new JdbcTemplate(datasource1);
}
In both cases it fails with:
Parameter 0 of method jdbcOperations in com.example.PersistentConfig required a single bean, but 2 were found:
- datasource1: defined in class path resource [datasources.xml]
- datasource2: defined in class path resource [datasources.xml]
Why the qualifier doesn't work?
I can't change the datasources.xml file to add a primary=true to datasource.
datasources.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="datasource1"
class="com.example.database.IdentifiedLazyConnectionDataSourceProxy">
<qualifier value="datasource1"/>
<property name="targetDataSource">
<bean class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/ak1Database"/>
<property name="resourceRef" value="true"/>
</bean>
</property>
<property name="identifier" value="shared"/>
</bean>
<bean id="datasource2"
class="com.example.database.IdentifiedLazyConnectionDataSourceProxy">
<qualifier value="datasource2"/>
<property name="targetDataSource">
<bean class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/ak2Database"/>
<property name="resourceRef" value="true"/>
</bean>
</property>
<property name="identifier" value="shared"/>
</bean>
</beans>

The reason this wasn't working is because xml configuration always overrides java config (see https://jira.spring.io/browse/SPR-7028).
To solve this I needed to create a bean that has different name then the one in the xml and mark that new bean as #Primary.
So now I will have three datasource beans, two connecting to the same database schema, but only one of the marked Primary so it will be used in default places instead of the xml defined one.

Related

BeanCurrentlyInCreationException Spring Boot

How I can fix this exception?
Without Spring Boot everything works.
So problem is in "App" section, am I right?
Caught exception
BeanCurrentlyInCreationException: Error creating bean with name 'pooledConnectionFactory':
Requested bean is currently in creation: Is there an unresolvable circular reference?
App
#ComponentScan
#EnableAutoConfiguration
#ImportResource("classpath:contexts/bean-locations.xml")
public class MyApp{
public static void main(String[] args) throws Exception {
new SpringApplication(MyApp.class).run(args);
}
}
Bean usage via import
<import resource="classpath*:contexts/database/datasource/base-data-source.xml"/>
<import resource="classpath*:contexts/database/datasource/pooled-data-source.xml"/>
First file from import:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<bean id="baseDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${connection.driverClassName}"/>
<property name="username" value="${connection.userName}"/>
<property name="password" value="${connection.password}"/>
<property name="url" value="${connection.url}"/>
</bean>
Second file from import:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<bean id="pool" class="org.apache.commons.pool.impl.GenericObjectPool">
<property name="minEvictableIdleTimeMillis"><value>300000</value></property>
<property name="timeBetweenEvictionRunsMillis"><value>60000</value></property>
</bean>
<bean id="dataSourceConnectionFactory" class="org.apache.commons.dbcp.DataSourceConnectionFactory">
<constructor-arg><ref bean="baseDataSource"/></constructor-arg>
</bean>
<bean id="pooledConnectionFactory" class="org.apache.commons.dbcp.PoolableConnectionFactory">
<constructor-arg index="0"><ref bean="dataSourceConnectionFactory"/></constructor-arg>
<constructor-arg index="1"><ref bean="pool"/></constructor-arg>
<constructor-arg index="2"><null/></constructor-arg>
<constructor-arg index="3"><null/></constructor-arg>
<constructor-arg index="4"><value>false</value></constructor-arg>
<constructor-arg index="5"><value>true</value></constructor-arg>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.PoolingDataSource"
depends-on="pooledConnectionFactory">
<constructor-arg><ref bean="pool"/></constructor-arg>
</bean>
Spring Boot has Database initialization feature which conflicts with your DataSource config. See DataSourceInitializer class. It tries to obtain data source before its creation.
To fix your app you can
Disable initializer using property
spring.datasource.initialize=false
Exclude data source auto config as you have your own configuration
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})

Spring unit testing and InitialContext

I would like to get a simple integration test with Tomcat and Spring 4 running, injecting a spring component and use my existing data source configuration from Tomcat. I don't want to configure my DataSource twice.
Everything, including my data source, is configured in the file WebContent/WEB-INF/application-context.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task"
xmlns:util="http://www.springframework.org/schema/util" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<task:annotation-driven />
<!-- annotations in bean classes -->
<!-- context:annotation-config / -->
<!-- mvc:annotation-driven / -->
<context:component-scan base-package="com.mycompany.package.**" />
<jpa:repositories base-package="com.mycompany.package.**" />
<!-- <aop:aspectj-autoproxy proxy-target-class="true"/> -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="jpaDialect" ref="jpaDialect" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="packagePU" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect" />
</property>
<property name="jpaPropertyMap">
<props>
<prop key="eclipselink.weaving">false</prop>
</props>
</property>
</bean>
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="showSql" value="true" />
</bean>
<bean id="jpaDialect"
class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect" />
<!-- <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource"/> </bean> -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="i18n/messages" />
</bean>
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/DefaultDB" />
</beans>
I am using this tutorial http://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#testcontext-ctx-management, but I don't fully understand how I can just run my test in the context of Tomcat that provides my DataSource.
Currently, I am this far:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = { "file:WebContent/WEB-INF/application-context.xml"})
public class SimulatorTest {
#Autowired
private ShiftSimulator simulator;
#BeforeClass
public void setup() {
// ???
}
#Test
public void testShiftStart() {
System.out.println("Hello world");
}
}
Currently, I get this error when running the test
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in URL [file:WebContent/WEB-INF/application-context.xml]: Cannot resolve reference to bean 'dataSource' while setting bean property 'dataSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource': Invocation of init method failed; nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource': Invocation of init method failed; nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
...
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
...
I fully understand that there needs to be a InitialContext, but how can I provide my test with my context that is provided by Tomcat, including my Data Source configuration? I don't want to set my data source connection credentials twice.
Solved it. Just to recap the problem: Spring tries to create the dataSource bean and fails at looking up the JNDI name - because there is no JNDI available.
So what I did for testing, I just created another XML files that overrides the bean dataSource. I ignored the file in version control and ask every developer to copy this file in place:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="com.sap.db.jdbc.Driver"
p:url="jdbc:sap://xxx:30015"
p:username="sa"
p:password="">
</bean>
</beans>
In the test class, I am providing the additional file reference that overrides dataSource so it never tries to do the failing JNDI lookup:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = { "file:WebContent/WEB-INF/application-context.xml", "file:WebContent/WEB-INF/test-datasource.xml" })
public class SimulatorTest {
...
TomcatJNDI offers a comfortable solution for this problem. It's based on Tomcat's JNDI system and initializes only Tomcat's JNDI environment without starting the server. So you can access all your resources as configured in Tomcat's configuration files in tests or from within any Java SE application. Usage is simple, e. g.
TomcatJNDI tomcatJNDI = new TomcatJNDI();
tomcatJNDI.processContextXml(contextXmlFile);
tomcatJNDI.start();
DataSource ds = (DataSource) InitialContext.doLookup("java:comp/env/path/to/datasource");
Find more about it here.

Unable autowire SessionFactory[null] into #Transactional bean

I have faced very strange issue with autowiring Hibernate SessionFactory bean into my Service.
I'm able to find SessionFactory bean in Spring Context object. So no issues with creating such bean.
But I can not autowire this bean into my service when it is marked with #Transactional annotation. Factory field is null.
Once I remove this annotation - everything works good.
Service Class:
#Service
#Transactional
public class ExampleRunner implements Runnable{
#Autowired
SessionFactory sessionFactory;
...
}
applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:property-placeholder location="classpath:application.properties"
ignore-resource-not-found="true" />
<context:component-scan base-package="org.edu" />
<context:annotation-config />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="${jdbc.driverClassName:org.hsqldb.jdbcDriver}" />
<property name="url" value="${jdbc.url:jdbc:hsqldb:mem:myAppDb}" />
<property name="username" value="${jdbc.username:sa}" />
<property name="password" value="$jdbc.password:}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="org.edu" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
${hibernate.dialect:org.hibernate.dialect.HSQLDialect}
</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto:create-drop}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql:true}</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="txManager"
proxy-target-class="true" />
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
Usage:
public static void main(String[] args) {
Runnable runner = new ClassPathXmlApplicationContext("applicationContext.xml").getBean(ExampleRunner.class);
runner.run();
}
Seems like when I make #Transactional as class level annotation Spring creates CGLib proxy bean where SessionFactory field is null.
But when I use #Transational as method level annotation everything works good.
So I just want to understand such behavior.
What have I missed from Spring documentation?
I use Spring 4.1.4.RELEASE.
I got your error actually why you getting problem here because you not allowing container to auto-wired bean for you as you using java Main method it will not auto-wired bean that's why you getting error.

Spring MVC controller's constructor

I am trying to put some initialization methods in controller's default constructor, but the problem that it never called. When I put an #Autowired annotation, the error is throuwn - Autowired annotation requires at least on argument.
What the best practice for putting some initialization code in one place except of putting it in each controller's method?
Thank you
#InitBinder
public void initBinder(WebDataBinder binder) {
try {
initialize();
Logger l = Logger.getLogger(this.getClass().getName());
l.warning("Init!!!");
} catch (Exception e) {
e.printStackTrace();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="de.butler.crm.controller" />
<mvc:annotation-driven />
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="l" />
</bean>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" >
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="de.butler.crm.resource.Resources" />
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="de" />
</bean>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="50000000"/>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
a) Controllers are just plain Spring Beans, so that all aspects of the Spring Bean lifecycle apply.
I.e. you can autowire properties or constructor parameters (with annotation support), you can initialize beans using the InitializingBean interface or a #PostConstruct method etc.
If none of this works, then there's something wrong with your setup and you'll have to post your web context xml and / or a stack trace.
b) If you need a per-request setup, then use the #InitBinder mechanism
By default life cycle of bean in spring is managed by Spring container .If you want to initialized your bean with some of your code you can do it with #postconstruct method.
You have to create listener for your bean and whenever your bean is initialized this two method will invoke #postconstruct and #predestroy so you have to use this method for doing things

Use Spring annotations to automatically apply Hibernate Interceptor?

In my service class I need the hibernate session available. I currently do this in the beans.xml:
<bean id = "userDao" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref bean="userDaoTarget" />
</property>
<property name="proxyInterfaces">
<value>com.app.dao.UserDao</value>
</property>
<property name="interceptorNames">
<list>
<value>hibernateInterceptor</value>
</list>
</property>
<qualifier value="proxy" />
</bean>
...
<bean id="hibernateInterceptor"
class="org.springframework.orm.hibernate3.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
<bean>
(copied by hand, may be some typos..)
I'm moving to using annotations over XML, I was wondering if there was a way to use them to configure the proxy as I have above including the hibernate interceptor? If not - is there a way that I can reduce the amount of XML (with about 7 DAOs it makes it very cluttered)
Ok, Let's go. You said
I am moving to using annotations over XML
Enable an aspect as follows
package br.com.ar.aop;
#Aspect
public class HibernateInterceptorAdvice {
#Autowired
private HibernateInterceptor hibernateInterceptor;
/**
* I suppose your DAO's live in com.app.dao package
*/
#Around("execution(* com.app.dao.*(..))")
public Object interceptCall(ProceedingJoinPoint joinPoint) throws Throwable {
ProxyFactory proxyFactory = new ProxyFactory(joinPoint.getTarget());
proxyFactory.addAdvice(hibernateInterceptor);
Class [] classArray = new Class[joinPoint.getArgs().length];
for (int i = 0; i < classArray.length; i++)
classArray[i] = joinPoint.getArgs()[i].getClass();
return
proxyFactory
.getProxy()
.getClass()
.getDeclaredMethod(joinPoint.getSignature().getName(), classArray)
.invoke(proxyFactory.getProxy(), joinPoint.getArgs());
}
}
But keep in mind It just works if your DAO's implements some interface (For instance, UserDAOImpl implements UserDAO). Spring AOP uses JDK dynamic proxy in this case. If you does not have any interface, you can rely on your IDE To refactor your code by using Extract interface
Declare your xml as follows (Be aware i am using Spring 2.5 xsd schema)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!--SessionFactory settings goes here-->
<bean class="org.springframework.orm.hibernate3.HibernateInterceptor">
<property name="sessionFactory" ref="sessionFactory"/>
<bean>
<!--To enable AspectJ AOP-->
<aop:aspectj-autoproxy/>
<!--Your advice-->
<bean class="br.com.ar.aop.HibernateInterceptorAdvice"/>
<!--Looks for any annotated Spring bean in com.app.dao package-->
<context:component-scan base-package="com.app.dao"/>
<!--Enables #Autowired annotation-->
<context:annotation-config/>
</beans>
Do not forget To put in the classpath besides Spring libraries
<SPRING_HOME>/lib/asm
<SPRING_HOME>/lib/aopalliance
<SPRING_HOME>/lib/aspectj
Have a look at the #Autowired annotation.

Categories