Java Migration Application context xml to #Beans - java

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.

Related

How to create DefaultMessageListenerContainer in Spring-Boot?

I am new to Spring-Boot and trying to create DefaultMessageListenerContainer so I can use the weblogic workmanager and run several message listeners in multithreaded fashion.
Can someone please provide some example.
So far, I found the below solution but how do I implement this in Spring-Boot?
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="receiver"/>
<property name="taskExecutor" ref="taskExecutor"/>
</bean>
Create a ConnectionFactory:
#Bean
public ActiveMQConnectionFactory receiverActiveMQConnectionFactory() {
ActiveMQConnectionFactory activeMQConnectionFactory =
new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL("yourBrokerUrl");
return activeMQConnectionFactory;
}
Create a DefaultJmsListenerContainerFactory:
#Bean
public DefaultJmsListenerContainerFactory orderDefaultJmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory
.setConnectionFactory(receiverActiveMQConnectionFactory());
factory.setConcurrency("3-10");
return factory;
}
Create your DefaultMessageListenerContainer:
#Bean
public DefaultMessageListenerContainer orderMessageListenerContainer() {
SimpleJmsListenerEndpoint endpoint =
new SimpleJmsListenerEndpoint();
endpoint.setMessageListener(new YourMessageListener());
endpoint.setDestination("yourDestination");
return orderDefaultJmsListenerContainerFactory()
.createListenerContainer(endpoint);
}
For a more detailed example checkout this post I created on Spring JMS listeners.

How to set bean using java config in spring

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());
}

How to inject values from Java Class to Datasource Bean in spring

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.

Spring 3's Quartz 2 implementation doesn't support requestsRecovery anymore

According to the Quartz 2's documentation
RequestsRecovery - if a job "requests recovery", and it is executing
during the time of a 'hard shutdown' of the scheduler (i.e. the
process it is running within crashes, or the machine is shut off),
then it is re-executed when the scheduler is started again. In this
case, the JobExecutionContext.isRecovering() method will return true.
Spring3 has support for both Quartz 1.x and Quartz 2.x. If we use Quartz 1.x then we need to use following configuration for creating JobDetail bean:
<bean id="ldapSynch" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.edfx.adb.scheduling.job.LDAPSynchronizer" />
<property name="requestsRecovery" value="true" />
</bean>
Internally org.springframework.scheduling.quartz.JobDetailBean extends org.quartz.JobDetail and in Quartz 1.x org.quartz.JobDetail has a setter public void setRequestsRecovery(boolean shouldRecover).
But if we use Quartz 2.x implementation of Spring3 then the aforesaid configuration changes as:
<bean id="ldapSynch" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.edfx.adb.scheduling.job.LDAPSynchronizer" />
<property name="requestsRecovery" value="true" />
</bean>
And also in Quartz 2.x there is no such org.quartz.JobDetail class, instead it is an interface and eventually org.springframework.scheduling.quartz.JobDetailFactoryBean doesn't takes the parameter <property name="requestsRecovery" value="true" /> anymore.
How could we pass this important parameter to the Quartz's scheduler?
After looking into the source code of the class org.springframework.scheduling.quartz.JobDetailFactoryBean and source code of Quartz 2.0 I have found that there is a kind of init method in org.springframework.scheduling.quartz.JobDetailFactoryBean which is public void afterPropertiesSet(); within this method the instance of org.quartz.JobDetail is being created. Fortunately this instance of org.quartz.JobDetail can be accessible via a method public JobDetail getObject() of the class org.springframework.scheduling.quartz.JobDetailFactoryBean.
In Quartz 2.0 the class org.quartz.impl.JobDeialImpl implements the interface org.quartz.JobDetail; so the instance of org.quartz.JobDetail in org.springframework.scheduling.quartz.JobDetailFactoryBean is actually the instance of org.quartz.impl.JobDeialImpl.
So I created a class com.edfx.adb.scheduling.ADBJobDetailFactoryBean which extends org.springframework.scheduling.quartz.JobDetailFactoryBean and overrides the afterPropertiesSet() method as:
package com.edfx.adb.scheduling;
import org.quartz.impl.JobDetailImpl;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
public class ADBJobDetailFactoryBean extends JobDetailFactoryBean {
private boolean requestsRecovery;
public ADBJobDetailFactoryBean() {
super();
}
#Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
JobDetailImpl jobDetail = (JobDetailImpl) getObject();
jobDetail.setRequestsRecovery(isRequestsRecovery());
}
public boolean isRequestsRecovery() {
return requestsRecovery;
}
public void setRequestsRecovery(boolean requestsRecovery) {
this.requestsRecovery = requestsRecovery;
}
}
And changed the spring bean configuration as:
<bean id="ldapSynch" class="com.edfx.adb.scheduling.ADBJobDetailFactoryBean">
<property name="jobClass" value="com.edfx.adb.scheduling.job.LDAPSynchronizer" />
<property name="requestsRecovery" value="true" />
</bean>
And voila. I have tested this by stopping running server while the scheduler was executing the task and when I restarted the server the scheduler was started executing the unfinished job.
Hope it will help someone.
#tapas-bose while searching for some quartz info I come across your post. It looks like Spring 3.2 support setting the property requestsRecovery. I don't know which version of Spring 3.? you have mentioned in your answer with which you faced the problem.
I set requestsRecovery property directly through setter injection and it worked for me.
<bean name="continousMonitoringProfileNotificationPushProcessing" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.mycompany.projectApp.jobs.ContinousMonitoringNotificationPushJobBean" />
<property name="jobDataAsMap">
<map>
<entry key="executionSuccessful" value="true"/>
</map>
</property>
<property name="durability" value="true" />
<property name="requestsRecovery" value="true"/>
</bean>
When I decompiled code for JobDetailFactoryBean it is overriding afterPropertiesSet method which creates JObDetailImpl instance and set requestsRecovery, (piece of decompiled code as below)
public class JobDetailFactoryBean implements FactoryBean<JobDetail>, BeanNameAware, ApplicationContextAware, InitializingBean
{
private String name;
private String group;
private Class jobClass;
private JobDataMap jobDataMap = new JobDataMap();
private boolean durability = false;
private boolean requestsRecovery = false;
...
..
..
public void afterPropertiesSet()
{
if (this.name == null) {
this.name = this.beanName;
}
if (this.group == null) {
this.group = "DEFAULT";
}
if (this.applicationContextJobDataKey != null) {
if (this.applicationContext == null) {
throw new IllegalStateException("JobDetailBean needs to be set up in ApplicationContext to be able to handle an 'applicationContextJobDataKey'");
}
getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext);
}
Class<?> jobDetailClass;
try
{
jobDetailClass = getClass().getClassLoader().loadClass("org.quartz.impl.JobDetailImpl");
}
catch (ClassNotFoundException ex) {
jobDetailClass = JobDetail.class;
}
BeanWrapper bw = new BeanWrapperImpl(jobDetailClass);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", this.name);
pvs.add("group", this.group);
pvs.add("jobClass", this.jobClass);
pvs.add("jobDataMap", this.jobDataMap);
pvs.add("durability", Boolean.valueOf(this.durability));
pvs.add("requestsRecovery", Boolean.valueOf(this.requestsRecovery));
So we don't need to manually extend the JobFactoryBean class and set the requestsRecovery property.

Dynamically create spring beans and alter properties on existing beans

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")));
}
}
}

Categories

Resources