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.
Related
I need to generate a file called: NEW_NAME_YYYY-MM-DD where YYYY-MM-DD is the current Date. Everything works fine except for when my batch tries to get the propertie tmp.dir it finds nothing and calls the directory null instead. This is the Writer class I am working with :
public class MyFileWriter extends FlatFileItemWriter {
#Value("${tmp.dir}")
private String tempDir;
public MyFileWriter(){
super();
setResource();
}
public void setResource() {
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD");
String stringDate = sdf.format(new Date());
String myPath = tempDir + "/NEW_NAME_" + stringDate + ".csv";
Resource outputFile = new FileSystemResource(myPath);
this.setResource(outputFile);
}
}
and this is the bean definition placed in applicationContext.xml along with the property-placeholder for the properties file:
<bean id="csvWriter" class="org.conters.writer.MyFileWriter" scope="step">
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value=";" />
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="ad,customer,total" />
</bean>
</property>
</bean>
</property>
<property name="encoding" value="${csv.encoding}" />
</bean>
Any idea on why the propertie isn't injected??? and as for the properties file called batch.properties in my case it is well declared in the applicationcontext.
Introduction
In order to extract some data from a database, I am trying to setup a basic hibernate and spring batch project. The goal is to provide one query (HQL) and based on this query the spring batch application extracts all the data to a flat file.
One of the requirements of the application is that the user should not have to configure the mappings of the columns. As such I am trying to create a DynamicRecordProcessor that evaluates the input and passes the input (a table for example Address) to the writer in such a way that the flat file item writer can use a PassThroughFieldExtractor.
Below the reader-processor-writer xml configuration:
<!-- Standard Spring Hibernate Reader -->
<bean id="hibernateItemReader" class="org.springframework.batch.item.database.HibernateCursorItemReader">
<property name="sessionFactory" ref="sessionFactory" />
<property name="queryString" value="from Address" />
</bean>
<!-- Custom Processor -->
<bean id="dynamicRecordProcessor" class="nl.sander.mieras.processor.DynamicRecordProcessor"/>
<!-- Standard Spring Writer -->
<bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:target/extract/output.txt" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="|"/>
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.PassThroughFieldExtractor"/>
</property>
</bean>
</property>
</bean>
EDIT:
And the job configuration:
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
<property name="cacheableMappingLocations" value="classpath*:META-INF/mappings/*.hbm.xml"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager" lazy-init="true">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Problem
My processor looks as following:
public class DynamicRecordProcessor<Input,Output> implements ItemProcessor<Input,Output> {
private static final String DELIMITER = "|";
private boolean areNamesSetup = false;
private List<String> names = new ArrayList<String>();
private Input item;
#SuppressWarnings("unchecked")
#Override
public Output process(Input item) throws Exception {
this.item = item;
initMapping();
return (Output) extract();
}
private void initMapping() {
if (!areNamesSetup) {
mapColumns();
}
areNamesSetup = true;
}
private void mapColumns() {
Field[] allFields = item.getClass().getDeclaredFields();
for (Field field : allFields) {
if (!field.getType().equals(Set.class) && Modifier.isPrivate(field.getModifiers())) {
names.add(field.getName());
}
}
}
private Object extract() {
List<Object> values = new ArrayList<Object>();
BeanWrapper bw = new BeanWrapperImpl(item);
for (String propertyName : this.names) {
values.add(bw.getPropertyValue(propertyName));
}
return StringUtils.collectionToDelimitedString(values, DELIMITER);
}
}
The table Address has the following field:
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="city_id", nullable=false)
public City getCity() {
return this.city;
}
And the corresponding column in city:
#Column(name="city_id", unique=true, nullable=false)
public Short getCityId() {
return this.cityId;
}
When using values.add(bw.getPropertyValue(propertyName)); with propertyName being "city" the following exception occurs:
org.hibernate.SessionException: proxies cannot be fetched by a stateless session
at org.hibernate.internal.StatelessSessionImpl.immediateLoad(StatelessSessionImpl.java:292)
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:156)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:260)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:68)
at nl.sander.mieras.localhost.sakila.City_$$_jvstc2c_d.toString(City_$$_jvstc2c_d.java)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at org.springframework.util.StringUtils.collectionToDelimitedString(StringUtils.java:1132)
at org.springframework.util.StringUtils.collectionToDelimitedString(StringUtils.java:1148)
at nl.sander.mieras.processor.DynamicRecordProcessor.extract(DynamicRecordProcessor.java:52)
at nl.sander.mieras.processor.DynamicRecordProcessor.process(DynamicRecordProcessor.java:27)
The value, however, is availabe as shown in the screenshot below.
Concrete question: How can I get the value 300?
I have tried getting the value using reflection API, but I couldn't reach the actual value I want to get...
Reproduce
I have setup a public repo. However, you still need a local database to be able to reproduce exactly the issue. https://github.com/Weirdfishees/hibernate-batch-example. Any suggestions how isolate this issue further are more then welcome.
Just flip your reader to be state-full instead of stateless with the useStatelessSession property:
<!-- Standard Spring Hibernate Reader -->
<bean id="hibernateItemReader" class="org.springframework.batch.item.database.HibernateCursorItemReader">
<property name="sessionFactory" ref="sessionFactory" />
<property name="queryString" value="from Address" />
<property name="useStatelessSession" value="false" />
</bean>
I'm getting four 'no setter found for property 'xxxx' in class com.rusapp.batch.trans.OLFMWriter'. A fifth bean in that class does not have an error, inputQueue. The rest have errors in the xml below at each of the property lines.
The beans appear as such:
<bean id="inputQueue" class="com.rusapp.batch.trans.OLFMWriter">
<property name="inputQueue" value="${${ENV}_MQ_FM_INPUT_QUEUE}" />
</bean>
<bean id="replyQueue" class="com.rusapp.batch.trans.OLFMWriter">
<property name="replyQueue" value="${${ENV}_MQ_FM_REPLY_QUEUE}" />
</bean>
<bean id="mqConnectionFactory" class="com.rusapp.batch.trans.OLFMWriter">
<property name="mqConnectionFactory" ref="mqConnection" />
</bean>
<bean id="JMSDestination"
class="com.rusapp.batch.trans.OLFMWriter">
<property name="JMSDestination" ref="jmsDestinationResolver" />
</bean>
<bean id="JMSReplyTo"
class="com.rusapp.batch.trans.OLFMWriter">
<property name="JMSReplyTo" ref="jmsDestinationResolverReceiver" />
</bean>
The setters in the class appear as follows:
public static void setMqConnectionFactory(MQConnectionFactory _mqConnectionFactory) {
OLFMWriter._mqConnectionFactory = _mqConnectionFactory;
}
public static void setReplyQueue(String _replyQueue) {
OLFMWriter._replyQueue = _replyQueue;
}
public static void setJMSDestination(Destination _JMSDestination) {
OLFMWriter._JMSDestination = _JMSDestination;
}
public static void setJMSReplyTo(Destination _JMSReplyTo) {
OLFMWriter._JMSReplyTo = _JMSReplyTo;
}
public void setInputQueue(String inputQueue){
_inputQueue = inputQueue;
}
This is not my code and I'm not too knowledgeable with Spring yet but I can't find anything wrong with the setter names. I thought it was a workspace error but they have persisted through several restarts of Eclipse.
Can anyone find any obvious faults with this code?
Your setters are static which means that they don't conform to the java beans specification.
I think you'll want to use a MethodInvokingFactorybean instead.
<bean abstract="true" id="abstractParent" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="com.rusapp.batch.trans.OLFMWriter"/>
</bean>
<bean id="inputQueue" parent="abstractParent">
<property name="staticMethod" value="setInputQueue" />
<property name="arguments">
<list><value>${${ENV}_MQ_FM_INPUT_QUEUE}</value></list>
</property>
</bean>
<bean id="replyQueue" parent="abstractParent">
<property name="staticMethod" value="setReplyQueue" />
<property name="arguments">
<list><value>${${ENV}_MQ_FM_REPLY_QUEUE}</value></list>
</property>
</bean>
etc...
I have been handling an application in which we are using LDAP to fetch user details. Sometimes it will take more time to fetch user details. I want to implement time out on methods that fetch details so that we can avoid hanging transactions in server in worst case.
Here we are using LdapUtil class in which we have configured LdapTemplate class to fetch the required details.
How can we implement timeout on LDAP methods?
(In this case ldapTemplate.search(...) methods)
public class LdapUtil {
#Autowired(required = true)
#Qualifier(value = "ldapTemplateApp")
LdapTemplate ldapTemplate;
public Set < ProductGroup > findProducts(String UserId) {
final Set < ProductGroup > products = newHashSet();
// Lookup the user
String usrFilter = String.format(USERID_FILTER, globalUserId);
ldapTemplate.search("ou=Members", usrFilter, // note this line
new NameClassPairCallbackHandler() {
public void handleNameClassPair(NameClassPair nameClassPair) {
SearchResult result = (SearchResult) nameClassPair;
String user = result.getNameInNamespace();
String GrpFilter = String.format(GROUP_FILTER, user);
List < String > zonePrefixes = ldapTemplate.search("Zones", GrpFilter, // note this line
new AttributesMapper() {
public Object mapFromAttributes(Attributes attributes) throws NamingException {
return substringBeforeLast((String) attributes.get("cn").get(), "-") + "-";
}
});
}
});
products.remove(null);
return newHashSet(products);
}
}
We have one LDAP.xml in which ldapTemplete is configured
<beans xmlns="------">
<!-- LDAP -->
<bean id="contextSourceApp" class="org.springframework.ldap.pool.factory.PoolingContextSource">
<property name="contextSource" ref="contextSourceTargetApp" />
<property name="dirContextValidator">
<bean id="dirContextValidator"
class="org.springframework.ldap.pool.validation.DefaultDirContextValidator"/>
</property>
<property name="testOnBorrow" value="true" />
</bean>
<bean id="contextSourceTargetApp" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="${ldap.url}" />
<property name="base" value="${ldap.base.}" />
<property name="userDn" value="${ldap.user}" />
<property name="password" value="${ldap.password}" />
<property name="pooled" value="false" />
</bean>
<bean id="ldapTemplateApp" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSourceApp" />
</bean>
I have few queries:
How can we implement the TIMEOUT for LDAP methods and how to configure it?(In which class of LDAP framework timeout settings will be there)
Is there any way to configure them in xml file i.e. LDAP.xml(in this case)?
I found a solution.
I have added the following property in my ldap.xml file. So far it worked for me.
<bean id="contextSourceTargetApp"
class="org.springframework.ldap.core.support.LdapContextSource">
<property name="baseEnvironmentProperties">
<map>
<entry key="com.sun.jndi.ldap.connect.timeout" value="5000" />
</map>
</property>
</bean>
Please post any other solution if you have any idea about LDAP timeout implementation.
For ActiveDirectoryLdapAuthenticationProvider the solution with ldap.xml file did not work for me. Instead I added a jndi.properties file to the classpath with the following content:
com.sun.jndi.ldap.connect.timeout=500
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")));
}
}
}