This might sound like a novice question. I want to inject datasource properties (which I am getting at runtime) and inject it to the bean..
I have a method in my javaclass...
public <String,String>map myMethod(Map<String, String> model) {
Map mapA = new HashMap();
mapA.put("username", "element 1");
mapA.put("password", "element 2");
mapA.put("host", "element 3");
return map;
}
I want to inject these values to my datasource bean in application-context.xml
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value=""/> // inject values here
<property name="url" />
<property name="username" />
<property name="password" />
</bean>
I have seen numerous example on injecting values to beans using properties file but I could not figure out on how to inject a value from java class to the bean properties.
Thanks
You need to create a #Configuration class with a method annotated with #Bean returning an instance of org.apache.commons.dbcp.BasicDataSource.
#Configuration
public class DatasourceConfiguration {
#Bean
public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(""); // you can call your code here
ds.setUrl(""); // to get these configuration values
ds.setUsername("");
ds.setPassword("");
return ds;
}
}
It can be a not so elegant solution, but what about this approach?
You can try to return a String from your method.
#Configuration
public class DatasourceConfiguration2 {
#Bean
public String getDataSourceSetting() {
Map<String, String> map = myMethod(model); //assuming that you are not able to edit the original method
StringBuilder sb = new StringBuilder();
for (Entry<String, String> e : map.entrySet()) {
sb.append(e.getKey()).append('=').append(e.getValue()).append(';');
}
}
}
In your xml you can define the property like:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="connectionProperties" value="dataSourceSetting"/>
</bean>
Based on dbcp api:
The "user" and "password" properties will be added explicitly, so they do not need to be included here.
Checking the source code you can see if user and password are null a message like log("DBCP DataSource configured without a 'username'"); will be printed. But the property will be available there.
Finally, in case of url property, there is no option, you need to set it up explicitly.
Related
Im trying to convert my context xml of hikari db setting to Beans method
<bean id="proxyConfig"
factory-bean="proxyConfigSupport"
factory-method="create"/>
<bean id="proxyConfigSupport" class="net.ttddyy.dsproxy.support.ProxyConfigSpringXmlSupport">
<property name="dataSourceName" value="dataSourceDefaultName"/>
<property name="queryListener" ref="queryListener"/>
</bean>
Im trying to understand how to convert it so I tried this way
#Bean(name = "proxyConfigSupport")
public ProxyConfigSpringXmlSupport proxyDataSource(ChainListener queryListener) {
ProxyConfigSpringXmlSupport proxyConfigSpringXmlSupport = new ProxyConfigSpringXmlSupport();
proxyConfigSpringXmlSupport.setDataSourceName("dataSourceDefaultName");
proxyConfigSpringXmlSupport.setQueryListener(queryListener);
return proxyConfigSpringXmlSupport;
}
But what does it mean "proxyConfig" factory-bean="proxyConfigSupport"
How do i set it the same ?
#Bean(name = "proxyConfig")
public ProxyConfig proxyConfig(ProxyConfigSpringXmlSupport proxyConfigSpringXmlSupport) {
ProxyConfig proxyConfig = new ProxyConfig();
return proxyConfig;
/*
<bean id="proxyConfig"
factory-bean="proxyConfigSupport"
factory-method="create"/>
*/
}
Lets divide the XML up into sections.
<bean id="proxyConfigSupport" class="net.ttddyy.dsproxy.support.ProxyConfigSpringXmlSupport">
<property name="dataSourceName" value="dataSourceDefaultName"/>
<property name="queryListener" ref="queryListener"/>
</bean>
You already got that bean method
#Bean
public ProxyConfigSpringXmlSupport proxyConfigSupport(ChainListener queryListener) {
ProxyConfigSpringXmlSupport proxyConfigSpringXmlSupport = new ProxyConfigSpringXmlSupport();
proxyConfigSpringXmlSupport.setDataSourceName("dataSourceDefaultName");
proxyConfigSpringXmlSupport.setQueryListener(queryListener);
return proxyConfigSpringXmlSupport;
}
Now the second one
<bean id="proxyConfig"
factory-bean="proxyConfigSupport"
factory-method="create"/>
Is nothing more then a description of what should take place. It defines a FactoryBean and specifies it do delegate the getObject() to the proxyConfigSupport bean and call the create() method. This is also explained in the Spring Framework Reference Guide.
As an #Bean method is also, kind of, a factory for beans, you can do something like this.
#Bean
public ProxyConfig proxyConfig(ProxyConfigSpringXmlSupport proxyConfigSupport) {
return proxyConfigSupport.create();
}
Nothing more and nothing less.
I have following XML bean configuration which I need to convert to Java based configuration
<bean id="baseBook" class="com.practice.Book" >
<property name="bookName" value="First book" />
</bean>
<bean id="bookBean" class="com.practice.ChildBook" parent="baseBook">
<property name="bookAuthor" value="Raj" />
<property name="bookPrice" value="200" />
</bean>
Something like below:
#Bean
public ChildBook bookBean(){
ChildBook bookBean = new ChildBook();
bookBean.setBookAuthor("Raj");
bookBean.setBookPrice(200);
return bookBean;
}
#Bean
public Book baseBook(){
Book book = new Book();
book.setBookName("First book");
return book;
}
However, I am not able to set bookName for ChildBook. I checked some solutions which suggests to create new init method and call it in bookBean(). But I am looking for more acurate/convenient solution. Thanks in advance!
I have below details in spring xml file. Now I want to convert it into spring java config bean.
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="test" />
<property name="port" value="111" />
<property name="username" value="test#gmail.com" />
<property name="password" value="test123" />
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.starttls.enable">true</prop>
</props>
</property>
</bean>
<bean id="utilityObject" class="com.ezone.utility.TestUtility">
<property name="mailSender" ref="mailSender" />
</bean>
Converted mailSender this bean as below. But How to convert utilityObject in java config spring bean. I am new in this.
#Bean(name="mailSender",autowire=Autowire.BY_NAME)
public JavaMailSenderImpl mailConfiguration(){
JavaMailSenderImpl mail = new JavaMailSenderImpl();
mail.setHost("test");
mail.setPort(111);
mail.setUsername("test#gmail.com");
mail.setPassword("test123");
Properties javaMailProperties = new Properties();
javaMailProperties.put("mail.smtp.auth", "true");
javaMailProperties.put("mail.smtp.starttls.enable", "true");
javaMailProperties.setProperty("mail.smtp.auth", "true");
javaMailProperties.setProperty("mail.smtp.starttls.enable", "true");
mail.setJavaMailProperties(javaMailProperties);
return mail;
}
How can I define below bean :
<bean id="utilityObject" class="com.ezone.utility.TestUtility">
<property name="mailSender" ref="mailSender" />
</bean>
The above bean has the reference of mailSender.
You can either put a parameter on the #Bean method, which will get injected:
#Bean
public TestUtility utilityObject(JavaMailSender mailConfiguration) {
return new TestUtility(mailConfiguration);
}
or call from one #Bean method in an #Configuration to another; Spring will proxy them and make sure the singleton behavior gets applied:
#Bean
public TestUtility utilityObject() {
return new TestUtility(mailConfiguration());
}
I think the first one is a bit less magic, but either approach should work.
The methods annotated with #Bean can be called from other methods. Spring makes proxy for #Configuration class and singletons are created only once.
#Bean
public TestUtility utilityObject() {
TestUtility uo = new TestUtility();
uo.setMailSender(mailConfiguration());
return uo;
}
See details http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-java-further-information-java-config
Use #configuration for the JavaMailSenderImpl class
Refer : http://www.tutorialspoint.com/spring/spring_java_based_configuration.htm
EDIT
#Bean
public TestUtility getUtilityObject() {
return new TestUtility(mailConfiguration());
}
I have created applicationContext with two PropertyPlaceholderConfigurer beans and accessing only one based on my input using context object. But, while accessing for the properties from "Service2Record" instance I am getting "Service1Record" properties values. Below is my sample code.
ApplicationContext.xml
<beans >
<context:component-scan base-package="com.test.record" />
<!-- Service1 Properties files -->
<bean id="service1"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations" >
<value>classpath:service_1.properties</value>
</property>
<property name="properties" >
<value>service1.class=com.test.record.ServiceRecord</value>
</property>
</bean>
<bean id="service1record" class="${service1.class}" />
<!-- Service2 Properties files -->
<bean id="service2"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<value>classpath:service_2.properties</value>
</property>
<property name="properties">
<value>service2.class=com.test.record.ServiceRecord</value>
</property>
</bean>
<bean id="service2record" class="${service2.class}" />
ServiceRecord Bean : -
#Configuration
public class ServiceRecord {
#Value("${request_queue_name}")
private String requestQueueName;
#Value("${reply_queue_name}")
private String replyQueueName;
public String getRequestQueueName() {
return requestQueueName;
}
public String getReplyQueueName() {
return replyQueueName;
}
}
Test Main Class -
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml");
ServiceRecord serviceRecord = null;
String inputService = "SERVICE2";
if(inputService.equals("SERVICE1")){
serviceRecord = (ServiceRecord)context.getBean("service1record");
} else {
serviceRecord = (ServiceRecord)context.getBean("service2record");
}
System.out.println(" RequestQueueName : " + serviceRecord.getRequestQueueName());
}
And
service_1.properties
request_queue_name=SERVICE1.REQUEST
reply_queue_name=SERVICE1.REPLY
service_2.properties
request_queue_name=SERVICE2.REQUEST
reply_queue_name=SERVICE2.REPLY
Here, every time output is "RequestQueueName : SERVICE2.REQUEST". Can you please tell how to get the respective values based on the properties file?
Modified -
I have single PPHC, within that setting multiple prop files for the location property and multiple propertieArray as below.
<property name="locations">
<list>
<value>classpath:common-service.properties</value>
<value>classpath:search-service.properties</value>
<value>classpath:update-service.properties</value>
</list>
</property>
<property name="propertiesArray" >
<list>
<value>common.class=com.xyz.rabbitmq.record.CommonUtil</value>
<value>search.class=com.xyz.rabbitmq.record.SearchRecord</value>
<value>update.class=com.xyz.rabbitmq.record.UpdateRecord</value>
</list>
</property>
<bean id="commonrecord" class="${common.class}"/>
<bean id="searchrecord" class="${search.class}"/>
<bean id="updaterecord" class="${update.class}"/>
Here, keys are different in each properties file and getting the bean instance based on the search or update request type.
serviceRecord = (ServiceRecord)context.getBean("searchrecord");
Will this approach is correct for loading different files?
I would suggest you to use different keys in the properties files:
service_1.properties
service1.request_queue_name=SERVICE1.REQUEST
service1.reply_queue_name=SERVICE1.REPLY
service_2.properties
service1.request_queue_name=SERVICE2.REQUEST
service1.reply_queue_name=SERVICE2.REPLY
And different ServiceRecord files, ServiceRecord1.java ServiceRecord2.java each reads properites from relevant properites file. i.e. when a new properties set/file is added you need to add a new ServiceRecord file.
If you don't want to have multiple ServiceRecords, you can instead have a Util method ..
public String getProperty(String serviceName, String propertyName) {
return propertySource.getProperty(serviceName+"."+propertyName);
}
The bean PropertyPlaceholderConfigurer is a BeanFactoryPostProcessor. Before the beans will be created, it is called and modify the whole context (the definitions that can change), in this case, only one bfpp can change something. Here you can read more about this.
I sucessfully managed to implement dynamic changing of database connections by following http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/ article.
But now the problem is, I have a list of database urls in a configuration file that is managed by a legacy application.
Is there a way to create beans in that Spring context from a list of values (i.e. Year2011DataSource, Year2012DataSource,...) and populate map of the dataSource bean with those beans that were just created?
<!-- Property file located in the legacy application's folder -->
<context:property-placeholder location="file:///D:/config.properties" />
<!-- Shared data source properties are read from the config.properties file -->
<bean id="parentDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" abstract="true">
<property name="driverClassName" value="${db.driver}" />
<property name="username" value="${db.user}" />
<property name="password" value="${db.password}" />
</bean>
<!-- Database urls by year -->
<bean id="Year2012DataSource" parent="parentDataSource">
<property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2012" />
</bean>
<bean id="Year2011DataSource" parent="parentDataSource">
<property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2011" />
</bean>
<bean id="Year2010DataSource" parent="parentDataSource">
<property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2010" />
</bean>
<!-- ... and so on, these should instead be populated dynamically ... -->
<!-- DbConnectionRoutingDataSource extends AbstractRoutingDataSource -->
<bean id="dataSource" class="someProject.DbConnectionRoutingDataSource">
<property name="targetDataSources">
<map key-type="int">
<entry key="2011" value-ref="Year2011DataSource" />
<entry key="2010" value-ref="Year2010DataSource" />
<!-- ... and so on, these also should instead be populated dynamically ... -->
</map>
</property>
<property name="defaultTargetDataSource" ref="Year2012DataSource" />
</bean>
A good fit for this requirement I think is a custom BeanFactoryPostProcessor - read in the legacy configuration and generate the datasources in the custom bean factory post processor:
class MyDatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//Read in details from legacy properties.. build custom bean definitions and register with bean factory
//for each legacy property...
BeanDefinitionBuilder datasourceDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicDataSource.class).addPropertyValue("url", "jdbc..");
beanFactory.registerBeanDefinition(datasourceDefinitionBuilder.getBeanDefinition());
}
}
As far as I know, there is no out-of-the-box solution using XML configuration. However, a simple solution to achieve this is described in this answer using FactoryBean abstraction in Spring.
I can tell you annotation approach. I would add urls and configuration in properties file and do something like following :
#Bean(name="dataSourceMap")
public Map<String, DataSource> dataSourceMap(DataSource dataSource2011, DataSource dataSource2012) {
// read properties from properties file and create map of datasource
Map<DataSource> map = new HashMap<>();
map.put("dataSource2011",dataSource2011);
map.put("dataSource2012",dataSource2012);
//map.put("dataSource2013",dataSource2013);
return map;
}
#Bean(name="dataSource2011",destroyMethod="close")
public DataSource dataSource() {
// read properties from properties file and create map of
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url2011);
dataSource.setUsername(username2011);
dataSource.setPassword(password2011);
return dataSource;
}
#Bean(name="dataSource2012",destroyMethod="close")
public DataSource dataSource() {
// read properties from properties file and create map of
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url2012);
dataSource.setUsername(username2012);
dataSource.setPassword(password2012);
return dataSource;
}
============================================
By following Biju's tip I've got everything working like this:
============================================
"Database urls by year" section in the spring configuration is no more, beans are created in the BeanFactoryPostProcessor.
"dataSource" bean has it's properties set to dummy data that is replaced in the BeanFactoryPostProcessor:
<bean id="dataSource" class="someProject.DbConnectionRoutingDataSource">
<property name="targetDataSources">
<map key-type="String">
<!-- Will be filled from the DatasourceRegisteringBeanFactoryPostProcessor -->
</map>
</property>
<property name="defaultTargetDataSource" value="java:jboss/datasources/ExampleDS" />
</bean>
And this is the BeanFactoryPostProcessor implementation:
#Component
class DatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
InitialContext ic = new InitialContext();
// read the list of available JNDI connections
NamingEnumeration<?> list = ic.listBindings(getJndiDSRoot());
HashSet<String> jndiDataSources = new HashSet<String>();
while (list.hasMore()) {
/*... (ommitted for simplicity) ...*/
connectionsList.put(key, value);
}
BeanDefinitionRegistry factory = (BeanDefinitionRegistry) beanFactory;
BeanDefinitionBuilder datasourceDefinitionBuilder;
// Create new beans
for (Entry<Integer, String> e : connectionsList.entrySet()) {
datasourceDefinitionBuilder = BeanDefinitionBuilder
.childBeanDefinition("parentDataSource")
.addPropertyValue("url", e.getValue());
factory.registerBeanDefinition("Year" + e.getKey() + "DataSource",
datasourceDefinitionBuilder.getBeanDefinition());
}
// Configure the dataSource bean properties
MutablePropertyValues mpv = factory.getBeanDefinition("dataSource").getPropertyValues();
// Here you can set the default dataSource
mpv.removePropertyValue("defaultTargetDataSource");
mpv.addPropertyValue("defaultTargetDataSource",
new RuntimeBeanReference("Year" + defaultYear + "DataSource"));
// Set the targetDataSource properties map with the list of connections
ManagedMap<Integer, RuntimeBeanReference> mm = (ManagedMap<Integer, RuntimeBeanReference>) mpv.getPropertyValue("targetDataSources").getValue();
mm.clear();
// Fill the map with bean references to the newly created beans
for (Entry<Integer, String> e : connectionsList.entrySet()) {
mm.put(e.getKey(), new RuntimeBeanReference("Year" + e.getKey() + "DataSource")));
}
}
}