how to convert jndi lookup from xml to java config - java

Currently I'm converting the xml to java config. But I stuck at some part that I have been research for several days. Here the problem:
Xml config:
<jee:jndi-lookup id="dbDataSource" jndi-name="${db.jndi}" resource-ref="true" />
<beans:bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate" >
<beans:property name="dataSource" ref="dbDataSource"></beans:property>
</beans:bean>
So far I managed to convert this code:
<jee:jndi-lookup id="dbDataSource" jndi-name="${db.jndi}" resource-ref="true" />
to this :
#Bean(name = "dbDataSource")
public JndiObjectFactoryBean dataSource() {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName("${db.jndi}");
bean.setResourceRef(true);
return bean;
}
And this :
<beans:bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate" >
<beans:property name="dataSource" ref="dbDataSource"></beans:property>
</beans:bean>
to this:
#Bean(name = "jdbcTemplate")
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
return jt;
}
The problem is the method setDataSource() need DataSource object but I'm not sure how to relate both bean.How to pass the JndiObjectFactoryBean to DataSource?
Or do I need to use another method?
Extra Question:
The bean.setJndiName("${db.jndi}") , ${db.jndi} is refer to properties file but I always got NameNotFoundException, How to make it work?
Thanks!!

Instead of JndiObjectFactoryBean use a JndiDataSourceLookup instead. To use the ${db.jndi} in the method declare a method argument and annotate it with #Value.
#Bean(name = "dbDataSource")
public DataSource dataSource(#Value("${db.jndi}") String jndiName) {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(jndiName);
}
Autowired methods and constructors can also use the #Value annotation. -- Spring Reference Guide.
#Bean methods are basically factory methods which are also auto wired methods and as such fall into this category.
In your factory method for the JdbcTemplate you can simply use a DataSource method argument to get a reference to the datasource (If you have multiple you can use the #Qualifier on the method argument to specify which one you want to use).
#Bean
public JdbcTemplate jdbcTemplate(DataSource ds) {
return new JdbcTemplate(ds);
}

Related

How to use transaction interceptor in a multi-tenant spring boot program?

I have a Spring Boot 2 + Hibernate 5 Multi-tenant application that connected to multiple oracle databases. when I use JpaRepository, everything is fine, ie after recieving http request, the 'Interceptor' detects the data source and the 'MultiTenantConnectionProvider' implementation selects the correct DataSource to be used by TransactionManager.
What is the problem?
I want to use TransactionInterceptor to handle transactions on some methods(with method names) from specific beans(that extends JdbcDaoSupport or inject JdbcDaoSupport implementations). Sorry if I explained a little confusing.
But for every http request, the data source of that beans is immutable(the default data source). while injected repositories operate on the correct data source within the correct transaction.
<bean id="txProxyTemplate" abstract="true"
depends-on="transactionManager"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
<bean id="genericJdbcDao" parent="txProxyTemplate">
<property name="target">
<bean class="org.broker.dao.impl.BaseGenericJdbcDAOImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</property>
</bean>
Can anyone help solve this problem?
I finally solved the problem. Follow the code section:
#Bean("jdbcTemplate")
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public JdbcTemplate jdbcTemplate(
#Qualifier("tenantIdentifierResolver") TenantIdentifierResolverImpl tenantResolver,
#Qualifier("tenantConnectionProvider") DataSourceMultiTenantConnectionProviderImpl dataSourceProvider) {
return new RhaJdbcTemplate(dataSourceProvider.selectDataSource(tenantResolver.resolveCurrentTenantIdentifier()));
}
#Bean(value = "transactionInterceptor")
public TransactionInterceptor transactionInterceptor(
#Qualifier("txProxyTemplateConfig") Properties txProxyTemplateConfig,
#Qualifier("transactionManager") PlatformTransactionManager transactionManager) {
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
transactionInterceptor.setTransactionAttributes(txProxyTemplateConfig);
transactionInterceptor.setTransactionManager(transactionManager);
return transactionInterceptor;
}
#Bean("genericJdbcDAO")
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public ProxyFactoryBean genericJdbcDAO(
#Qualifier("jdbcTemplate") JdbcTemplate jdbcTemplate) throws ClassNotFoundException {
BaseGenericJdbcDAOImpl genericJdbcDAO = new BaseGenericJdbcDAOImpl();
genericJdbcDAO.setJdbcTemplate(jdbcTemplate);
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(genericJdbcDAO);
proxyFactoryBean.setProxyInterfaces(new Class[] {BaseGenericJdbcDAO.class});
proxyFactoryBean.setInterceptorNames("transactionInterceptor");
return proxyFactoryBean;
}
Then inject BaseGenericJdbcDAO into the controller, as a sample:
#RestController
public class VoucherLineRest {
#Resource(name = "genericJdbcDAO")
private BaseGenericJdbcDAO genericJdbcDAO;
}
You will see that for every request the correct data source selected and TransactionInterceptor intercept every method called from BaseGenericJdbcDAO bean and apply the correct transaction propagation on it.
I hope you enjoy this experience.

How to translate xml configuration bean to Java config bean?

I am rewriting a legacy project using Quartz and Spring Framework. The original configuration is in XML and now I am translating it into Java Config. The xml configuration uses jobDetail to set the job detail property of the trigger bean. However, when I try to use the equivalent method, i.e. the setter: setJobDetails(simpleJobDetail), I got a warning that the setter does not have correct type (expecting JobDetails, but got MethodInvokingJobDetailFactoryBean).
May I know whether it is correct to translate xml bean configuration to Java COnfig by using the equivalent named setter in Java COnfig?
Why in the XML property setting, the trigger bean can set its jobDetail property as the simpleJobDetail bean (which has type MethodInvokingJobDetailFactoryBean) , while the Java Config could not?
XML config:
<!-- For times when you just need to invoke a method on a specific object -->
<bean id="simpleJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="reader" />
<property name="targetMethod" value="readData" />
</bean>
<!-- Run the job every 60 seconds with initial delay of 1 second -->
<bean id="trigger"
class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="simpleJobDetail" />
<property name="repeatInterval" value="600000" />
</bean>
Java Config:
#Bean
public MethodInvokingJobDetailFactoryBean simpleJobDetail() {
MethodInvokingJobDetailFactoryBean simpleJobDetail = new MethodInvokingJobDetailFactoryBean();
simpleJobDetail.setTargetObject(reader());
simpleJobDetail.setTargetMethod("readData");
return simpleJobDetail;
}
#Bean
private Object reader() {
// TODO: 10/13/2016
return null;
}
#Bean
public SimpleTriggerFactoryBean trigger() {
final SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
trigger.setJobDetail( simpleJobDetail()); // got warning about wrong type
trigger.setRepeatInterval(60000);
return trigger;
}
Note that simpleJobDetail() returns a factory, not the bean itself. You can rely on autowiring to inject a JobDetail built using this factory.
#Bean
public SimpleTriggerFactoryBean trigger(final JobDetail simpleJobDetail) {
final SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
trigger.setJobDetail(simpleJobDetail); // got warning about wrong type
trigger.setRepeatInterval(60000);
return trigger;
}
Hope it helps.

No transaction when using Spring websocket

I have configured a websocket with #MessageMapping annotation as usual. It works perfectly but when I call to some method surrounded with #Transactional and I try to executed some updated over the database I get an error:
javax.persistence.TransactionRequiredException: Executing an update/delete query.
It seems the transactional manager it is not working in this case. However, when this same method is invoked from a Rest controller everything goes well. Anybody knows what is happening?
Thanks
I replaced the XML config:
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
with JAVA config:
#Configuration
#EnableTransactionManagement
public class AppConfig {
#Autowired
private EntityManagerFactory entityManagerFactory;
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory);
}
}
and it works.

Spring MVC #PropertySource all Key/value as a map

In my Spring MVC application, I want to read ALL key/values from a specify properties file.
I am including the properties file to my java class by
#PropertySource("classpath:user-form-validation-configuration.properties")
and can read a one key at a time
#Autowired
Environment env;
and env.getProperty("userIdEmail")
Please help me how to get all key/value as a map
Thanks
Manu
One way to achieve the same is Spring: access all Environment properties as a Map or Properties object and secondly is:
<bean id="myProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:user-form-validation-configuration.properties"/>
</bean>
For, Annotation based:
#Bean(name = "myProperties")
public static PropertiesFactoryBean mapper() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource(
"user-form-validation-configuration.properties"));
return bean;
}
Then you can pick them up in your application with:
#Resource(name = "myProperties")
private Map<String, String> myProperties;

Populating a spring bean using a constructor-arg field

How can i inject a properties file containing a Map to be used as additional constructor arg using the field.
With a Map being loaded from a properties file
the bean is currently setup using:
<bean id="graphDbService" class="org.neo4j.kernel.EmbeddedGraphDatabase"
init-method="enableRemoteShell" destroy-method="shutdown">
<constructor-arg index="0" value= "data/neo4j-db"/>
<constructor-arg index="1" value=? />
</bean>
Java Equivalent:
Map<String,String> configuration = EmbeddedGraphDatabase.loadConfigurations( "neo4j_config.props" );
GraphDatabaseService graphDb = new EmbeddedGraphDatabase( "data/neo4j-db", configuration );
Thanks
Something like this:
<bean id="configuration" class="org.neo4j.kernel.EmbeddedGraphDatabase"
factory-method="loadConfigurations">
<constructor-arg value="neo4j_config.props"/>
</bean>
<bean id="graphDbService" class="org.neo4j.kernel.EmbeddedGraphDatabase"
init-method="enableRemoteShell" destroy-method="shutdown">
<constructor-arg index="0" value="data/neo4j-db"/>
<constructor-arg index="1" ref="configuration" />
</bean>
This takes advantage of the ability to create beans using arbitrary static factory methods, in this case using loadConfigurations() as a factory method to create the configuration bean, which is then injected into the proper constructor of EmbeddedGraphDatabase.
Create a bean that loads the properties (and takes the file name as an argument) and inject that instead.
EDIT When using annotations, things like constructor injection become more simple:
#Bean
public Map<String,String> configuration() {
return EmbeddedGraphDatabase.loadConfigurations( "neo4j_config.props" );
}
#Bean
public GraphDatabaseService graphDb() {
return new EmbeddedGraphDatabase( "data/neo4j-db", configuration() );
}
Note that the second bean definition method "simply" calls the first. When this code is executed, Spring will do some magic so you can still override the bean elsewhere (i.e. beans still overwrite each other) and it will make sure that the method body will be executed only once (no matter how often and from where it was called).
If the config is in a different #Configuration class, then you can #Autowired it:
#Autowired
private Map<String,String> configuration;
#Bean
public GraphDatabaseService graphDb() {
return new EmbeddedGraphDatabase( "data/neo4j-db", configuration );
}

Categories