write method in spring batch not called - spring

I have a problem whereby the write method is not called.
Spring Context
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:batch="http://www.springframework.org/schema/batch" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch-2.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
">
<!-- spring batch context -->
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager" />
</bean>
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<!-- Must set this -->
<bean
class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
<bean id="jobRegistry"
class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<!-- spring batch context -->
<bean id="report" class="core.Report" scope="prototype" />
<bean id="customWriter" class="spring.jobs.CustomWriter" />
<batch:job id="reportJob">
<batch:step id="step1">
<batch:tasklet>
<batch:chunk reader="cvsFileItemReader" writer="customWriter"
commit-interval="10">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="cvsFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<!-- Read a csv file -->
<property name="resource" value="file:input/report.csv" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="id,impressions" />
</bean>
</property>
<property name="fieldSetMapper">
<bean
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="report" />
</bean>
</property>
</bean>
</property>
</bean>
<!-- run every 5 seconds -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<bean id="cronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="*/5 * * * * ?" />
</bean>
</property>
</bean>
<bean id="jobDetail"
class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="quartz.JobLauncherDetails" />
<property name="group" value="quartz-batch" />
<property name="durability" value="true" />
<property name="jobDataAsMap">
<map>
<entry key="jobName" value="reportJob" />
<entry key="jobLocator" value-ref="jobRegistry" />
<entry key="jobLauncher" value-ref="jobLauncher" />
<entry key="param1" value="j1" />
<entry key="param2" value="j2" />
</map>
</property>
</bean>
</beans>
CustomWriter.java
import java.util.List;
import org.springframework.batch.item.ItemWriter;
import core.Report;
public class CustomWriter implements ItemWriter<Report> {
#Override
public void write(List<? extends Report> items) throws Exception{
System.out.println("writer..." + items.size());
for(Report item : items){
System.out.println(item);
}
}
}
quartz.JobLauncherDetails class
package quartz;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import org.quartz.JobExecutionContext;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class JobLauncherDetails extends QuartzJobBean {
static final String JOB_NAME = "jobName";
private JobLocator jobLocator;
private JobLauncher jobLauncher;
public JobLauncherDetails() {
System.out.println("JobLauncherDetails initialize...");
}
public void setJobLocator(JobLocator jobLocator) {
this.jobLocator = jobLocator;
}
public void setJobLauncher(JobLauncher jobLauncher) {
this.jobLauncher = jobLauncher;
}
#SuppressWarnings("unchecked")
protected void executeInternal(JobExecutionContext context) {
System.out.println("executeInternal...");
Map<String, Object> jobDataMap = context.getMergedJobDataMap();
String jobName = (String) jobDataMap.get(JOB_NAME);
JobParameters jobParameters = getJobParametersFromJobMap(jobDataMap);
try {
jobLauncher.run(jobLocator.getJob(jobName), jobParameters);
} catch (JobExecutionException e) {
e.printStackTrace();
}
}
// get params from jobDataAsMap property, job-quartz.xml
private JobParameters getJobParametersFromJobMap(
Map<String, Object> jobDataMap) {
JobParametersBuilder builder = new JobParametersBuilder();
for (Entry<String, Object> entry : jobDataMap.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (value instanceof String && !key.equals(JOB_NAME)) {
builder.addString(key, (String) value);
} else if (value instanceof Float || value instanceof Double) {
builder.addDouble(key, ((Number) value).doubleValue());
} else if (value instanceof Integer || value instanceof Long) {
builder.addLong(key, ((Number) value).longValue());
} else if (value instanceof Date) {
builder.addDate(key, (Date) value);
} else {
// JobDataMap contains values which are not job parameters
// (ignoring)
}
}
// need unique job parameter to rerun the same job
builder.addDate("run date", new Date());
return builder.toJobParameters();
}
}
In this case, the writer.... in write method is not printed.. Couldn't find the problem up till now..

I tried your config with latest version of batch (2.2.1.RELEASE), spring (3.2.3.RELEASE) and quartz (2.2.0) and all works fine substituting file:input/report.csv with file:${user.dir}/src/main/java/input/report.csv.
Are you sure file exists and doesn't throw a
IllegalStateException: Input resource must exist (reader is in
'strict' mode): URL [file:input/report.csv]
You can try use classpath:input/report.csv or build the real path of file (or check if your file is not empty...)

Related

Spring Batch MultiReader

I am trying to for multi-read using spring batch approach in which i have to get result from multiple queries and need to do some processing withe result and write them into file. I tried for a sample code but its giving me a error. I believe i have almost there but something i am missing due to my limited knowledge in spring batch
Below is my Code
MultipleReader-batch-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:batch="http://www.springframework.org/schema/batch" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<import resource="classpath:context-datasource.xml" />
<!-- JobRepository and JobLauncher are configuration/setup classes -->
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id= "result1" class="org.springframework.batch.item.database.JdbcCursorItemReader" >
<property name="dataSource" ref="dataSource" />
<property name="sql" value="SELECT * from SNG_CORE.LOOK_UP" />
<property name="rowMapper"> <bean class="com.websystique.springbatch.ExamResultRowMapper" />
</property>
</bean>
<bean id ="result2" class="org.springframework.batch.item.database.JdbcCursorItemReader" >
<property name="dataSource" ref="dataSource" />
<property name="sql" value= "SELECT * from SNG_CORE.LOOK_UP_2"/>
<property name="rowMapper"> <bean class="com.websystique.springbatch.ExamResultRowMapper" />
</property>
</bean>
<bean id="customItemReader" class="com.websystique.springbatch.MyCustomReader" scope="step">
<property name="result1" ref="result1" />
<property name="result2" ref="result2" />
</bean>
<job id="MultipleReaderJob" xmlns="http://www.springframework.org/schema/batch">
<step id="step1">
<tasklet>
<chunk reader="customItemReader" writer="flatFileItemWriter"
commit-interval="1" />
</tasklet>
</step>
<property name="resource" value="file:csv/examResult.txt" />
<property name="lineAggregator">
<!-- An Aggregator which converts an object into delimited list of strings -->
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="|" />
<property name="fieldExtractor">
<!-- Extractor which returns the value of beans property through reflection -->
<bean
class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="studentName, studentName1, studentName2" />
</bean>
</property>
</bean>
</property>
</bean>
====
ExamResultRowMapper
public class ExamResultRowMapper implements RowMapper<ExamResult>{
#Override
public ExamResult mapRow(ResultSet rs, int rowNum) throws SQLException {
ExamResult result = new ExamResult();
result.setStudentName(rs.getString("I_LKUP"));
result.setStudentName1(rs.getString("C_LKUP_TYP"));
result.setStudentName2(rs.getString("C_LKUP_KEY"));
return result;
}
==================
Custom Reader
public class MyCustomReader implements ItemReader<ExamResult> {
ItemReader<ExamResult> result1;
ItemReader<ExamResult> result2;
#Override
public ExamResult read()
throws Exception, UnexpectedJobExecutionException, ParseException, NonTransientResourceException {
ExamResult application1 = result1.read();
ExamResult application2 = result2.read();
/***
* I want to perform some operation with two result set
* and passs the final data to writter
* /
ExamResult summary = new ExamResult();
return summary;
}
}
===Exam result===
public class ExamResult {
private String studentName;
private String studentName1;
private String studentName2;
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentName1() {
return studentName1;
}
public void setStudentName1(String studentName1) {
this.studentName1 = studentName1;
}
public String getStudentName2() {
return studentName2;
}
public void setStudentName2(String studentName2) {
this.studentName2 = studentName2;
}
#Override
public String toString() {
return " [studentName=" + studentName + ", dob=" + studentName1 + ", Std=" + studentName2 + "]";
}
}
====================================================================
When ever i execute this i am getting
Encountered an error executing step step1 in job MultipleReaderJob
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.customItemReader' defined in class path resource [MultipleReader-batch-context.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'result1' of bean class [com.websystique.springbatch.MyCustomReader]: Bean property 'result1' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
Note : All works good if i run my job to read with 1 result set .
<job id="MultipleReaderJob" xmlns="http://www.springframework.org/schema/batch">
<step id="step1">
<tasklet>
<chunk reader="result1" writer="flatFileItemWriter"
commit-interval="1" />
</tasklet>
</step>

Spring Batch partition doesnt work composite itemprocessor

I have a Spring Batch partition job. I’m using CompositeProcessor, read data from DB and save these items into an CopyOnWriteArrayList. Because the environment is concurrent but my CopyOnWriteArrayList is being utilized for other threads and mix information, I don’t know why and what I am doing bad, and the output writing them into files for each thread.
public class CustomerItemProcessor implements ItemProcessor<beangenerico,CopyOnWriteArrayList<beanCustomer>> {
private CustomerDAO customerDAO;
private CopyOnWriteArrayList<beanCustomer> listbean;
public CopyOnWriteArrayList<beanCustomer> process(beangenerico rangos) throws Exception {
listbean = customerDAO.getAccAgentes(rangos);
if(listbean != null) {
//customer.setId(currentCustomer.getId());
return listbean;
}else{
return null;
}
}
The configuration of my batch im XML:
<batch:job id="partitionJob" xmlns="http://www.springframework.org/schema/batch">
<batch:step id="masterStep">
<batch:partition step="slave" partitioner="rangePartitioner">
<batch:handler grid-size="10" task-executor="taskExecutor"/>
</batch:partition>
</batch:step>
</batch:job>
<!-- each thread will run this job, with different stepExecutionContext values. -->
<batch:step id="slave" xmlns="http://www.springframework.org/schema/batch">
<batch:tasklet task-executor="taskExecutor" throttle-limit="1">
<batch:chunk reader="beaniniendreader" writer="tempRecordsWriter" processor="completeItemProcessor" commit-interval="1" />
</batch:tasklet>
</batch:step>
<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
<bean id="rangePartitioner" class="my.package.springbatch.RangePartitioner" />
<bean id="beaniniendreader" class="my.package.springbatch.FormiikReader" scope="step"></bean>
<bean id="beanprocessor" class="my.package.springbatch.FormiikProcessor" scope="step">
<property name="accountExecutiveDao" ref="accountExecutiveDao"/>
</bean>
<bean id="beanprocessor2" class="my.package.springbatch.CustomerItemProcessor" scope="step">
<property name="customerDAO" ref="customerAccDao"/>
</bean>
<bean id="completeItemProcessor" class="org.springframework.batch.item.support.CompositeItemProcessor">
<property name="delegates">
<list>
<ref bean="beanprocessor2"/>
<ref bean="accItemprocessor"/>
<ref bean="beanaccDataItem"/>
</list>
</property>
</bean>
<bean id="tempRecordsWriter" class="my.package.springbatch.ListDelegateWriter" scope="step">
<property name="delegate" ref="flatFileItemWriterPartition"/>
</bean>
<!-- csv file writer -->
<bean id="flatFileItemWriterPartition" class="org.springframework.batch.item.file.FlatFileItemWriter"
scope="step" >
<property name="resource"
value="file:csv/outputs/users.processed#{stepExecutionContext[fromId]}-#{stepExecutionContext[toId]}.csv" />
<property name="appendAllowed" value="false" />
<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="cuenta, name, purchasedPackage" />
</bean>
</property>
</bean>
</property>
</bean>
I going back to take the subject of my code, it is advised to me using Threadlocal for storing thread-specific data whereby it´s works . Here I put my code again. Thanks for your replies.
public class CustomerItemProcessor implements ItemProcessor<beangenerico,ThreadLocal<CopyOnWriteArrayList<beanCustomer>>> {
private CustomerDAO customerDAO;
private ThreadLocal<CopyOnWriteArrayList<beanCustomer>> listbean = new ThreadLocal<CopyOnWriteArrayList<beanCustomer>>();
public ThreadLocal<CopyOnWriteArrayList<beanCustomer>> process(beangenerico rangos) throws Exception {
listbean.set(new CopyOnWriteArrayList<beanCustomer>());
listbean = customerDAO.getAccAgentes(rangos);
if(listbean != null) {
return listbean;
} else {
return null;
}
}
public void setCustomerDAO(CustomerDAO customerDAO) {
this.customerDAO = customerDAO;
}
}

No value for key [org.springframework.ldap.core.support.LdapContextSource#27bd27bd] bound to thread [main]

I'm trying to implement LDAP transaction using Spring.I have configured the spring xml based on the spring Doc, but while running the program i'm getting below mentioned error:
java.lang.IllegalStateException: No value for key [org.springframework.ldap.core.support.LdapContextSource#27bd27bd] bound to thread [main]
at org.springframework.transaction.support.TransactionSynchronizationManager.unbindResource(TransactionSynchronizationManager.java:199)
at org.springframework.transaction.compensating.support.AbstractCompensatingTransactionManagerDelegate.doCleanupAfterCompletion(AbstractCompensatingTransactionManagerDelegate.java:122)
at org.springframework.ldap.transaction.compensating.manager.ContextSourceTransactionManager.doCleanupAfterCompletion(ContextSourceTransactionManager.java:135)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager.java:1011)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:877)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:822)
at org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionAspectSupport.java:411)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:114)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy11.createUser(Unknown Source)
Note:I'm trying to rollback LDAP as well as DB.
Code snippet:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.apps.ldap.manager.UserManger;
import com.apps.ldap.model.UserBean;
public class SpringFrameworkLDAPClient {
private static ApplicationContext context = null;
public static void main(String[] args) {
try {
context = new ClassPathXmlApplicationContext("conf/springldap.xml");
UserManger userservice = (UserManger)context.getBean("userManger");
UserBean userbean = new UserBean();
userbean.setCommonName("Santhosh5");
userbean.setLastName("Rajendran5");
userbean.setEmail("rsanthoshkumarr#gmail.com");
userbean.setFirstName("Santhosh5");
userbean.setUserID("Santhosh5");
userservice.createuser(userbean);
} catch (Exception e) {
System.out.println("Error occured " + e);
e.printStackTrace();
}
}
}
UserService
#Transactional
public void createuser(UserBean contactDTO) {
Attributes personAttributes = new BasicAttributes();
BasicAttribute personBasicAttribute = new BasicAttribute("objectclass");
personBasicAttribute.add("person");
personAttributes.put(personBasicAttribute);
personAttributes.put("cn", contactDTO.getCommonName());
personAttributes.put("sn", contactDTO.getLastName());
DistinguishedName newContactDN = new DistinguishedName("ou=users");
newContactDN.add("cn", contactDTO.getCommonName());
ldapTemplate.bind(newContactDN, null, personAttributes);
}
XML:
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="ldap://xxxx:389" />
<property name="base" value="dc=xxx" />
<property name="userDn" value="cn=xxx" />
<property name="password" value="xxx" />
</bean>
<bean id="contextSource" class="org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy">
<constructor-arg ref="contextSourceTarget" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSource" />
</bean>
<bean id="transactionManager" class="org.springframework.ldap.transaction.compensating.manager.ContextSourceTransactionManager">
<property name="contextSource" ref="contextSource" />
</bean>
<bean id="contactDao" class="com.apps.ldap.daos.impl.Userservice">
<property name="ldapTemplate" ref="ldapTemplate" />
</bean>
<bean id="myDataAccessObject" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="contactDao" />
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRES_NEW</prop>
</props>
</property>
</bean>
JARs used:
apache-commons-lang.jar
commons-logging-1.1.1.jar
ibatis-2.3.0.677.jar
jt400.jar
ldapbp-1.0.jar
org.springframework.aop-3.0.5.RELEASE.jar
org.springframework.asm-3.0.5.RELEASE.jar
org.springframework.beans-3.0.5.RELEASE.jar
org.springframework.context-3.0.5.RELEASE.jar
org.springframework.expression-3.0.5.RELEASE.jar
org.springframework.jdbc-3.0.5.RELEASE.jar
org.springframework.oxm-3.0.5.RELEASE.jar
org.springframework.test-3.0.5.RELEASE.jar
org.springframework.transaction-3.0.5.RELEASE.jar
org.springframework.web-3.0.5.RELEASE.jar
spring-core-3.0.4.RELEASE.jar
spring-ldap-1.1.2.jar
spring-ldap-core-1.3.2.RELEASE.jar
spring.jar
SpringUtils-0.2.jar

How to create a SocketConfig class in Spring using factory-method?

I am using apache commons httpclient 4.3.x along with spring3. I am trying to wire up a connectionpool with it's associated socketconfig instance.
http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.html
http://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/config/SocketConfig.html?is-external=true
My code looks like this:
<bean id="socketConfig" class="org.apache.http.config.SocketConfig" factory-method="custom" init-method="build">
<property name="soTimeout" value="60000"/>
<property name="soLinger" value="5" />
</bean>
<bean name="poolingHttpConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager" depends-on="socketConfig">
<property name="maxTotal" value="20" />
<property name="defaultMaxPerRoute" value="20" />
<property name="defaultSocketConfig" ref="socketConfig" />
</bean>
However, this is not working. The instance type that is used to setDefaultSocketConfig() on PoolingHttpClientConnectionManager is of type SocketConfig.Builder instead of SocketConfig.
What I want to have happen is as follows:
SocketConfig config = SocketConfig.custom()
.setSoTimeout(60000)
.setSoLinger(5)
.build()
So, I expect that the socketConfig bean type should be a SocketConfig instance, not a SocketConfig.Builder instance.
As per spring docs, I thought this should work.
http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#beans-factory-class-static-factory-method
is there anything I am doing wrong? Or is this just not supported in spring?
It turns out that the socketconfig builder instance is not designed to work with spring very well.
I had to use a spring beanfactory implementation to create the instance.
The bean class:
import org.apache.http.config.SocketConfig;
import org.springframework.beans.factory.FactoryBean;
public class SocketConfigFactoryBean implements FactoryBean<SocketConfig> {
int soLinger;
int soTimeout;
public SocketConfig getObject() throws Exception {
return SocketConfig.custom()
.setSoLinger(soLinger)
.setSoTimeout(soTimeout)
.build();
}
public Class<?> getObjectType() {
return SocketConfig.class;
}
public boolean isSingleton() {
return true;
}
public int getSoLinger() {
return soLinger;
}
public void setSoLinger(int soLinger) {
this.soLinger = soLinger;
}
public int getSoTimeout() {
return soTimeout;
}
public void setSoTimeout(int soTimeout) {
this.soTimeout = soTimeout;
}
}
The bean definition
<bean name="poolingHttpConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager">
<property name="maxTotal" value="20" />
<property name="defaultMaxPerRoute" value="20" />
<property name="defaultSocketConfig">
<bean class="org.apache.http.config.SocketConfig" factory-method="custom" init-method="build">
<bean class="com.ex.spring.beans.factory.SocketConfigFactoryBean">
<property name="soTimeout" value="60000"/>
<property name="soLinger" value="5" />
</bean>
</property>
</bean>
I was able to achieve it by doing the next configuration in spring:
<bean id="socketConfig" class="org.apache.http.config.SocketConfig" factory-method="custom">
<property name="soTimeout" value="1000" />
<property name="soLinger" value="5" />
</bean>
<bean name="poolingHttpConnectionManager" class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager">
<property name="maxTotal" value="20" />
<property name="defaultMaxPerRoute" value="20" />
<property name="defaultSocketConfig">
<bean factory-bean="socketConfig" factory-method="build" />
</property>
</bean>

How to merge PropertiesFactoryBean?

I have two spring profiles: development and production.
There should be a common properties file (placed in classpath:properties/common/*.properties) which will be overridden by the profile's properties file (placed in classpath:properties/development/*.properties.
This is my context config snippet to clarify my intention:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<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="com.example.entities.*" />
<property name="hibernateProperties" ref="hibernateProperties" />
</bean>
<beans profile="development">
<context:property-placeholder location="classpath:properties/development/jdbc.properties" />
<bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="ignoreResourceNotFound" value="true" />
<property name="location" value="classpath:properties/development/hibernate.properties" />
</bean>
</beans>
<beans profile="production">
<context:property-placeholder location="classpath:properties/production/jdbc.properties" />
<bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="ignoreResourceNotFound" value="true" />
<property name="location" value="classpath:properties/production/hibernate.properties" />
</bean>
</beans>
Currently there's no common properties somewhere. How to merge a common properties file with the one in each profile for both jdbc.properties and hibernate.properties?
I've used JavaConfig for this:
#Configuration
#Profile("development")
public class DevelopmentConfig {
public #Bean String profile() {
return "development";
}
}
#Configuration
#Profile("production")
public class ProductionConfig {
public #Bean String profile() {
return "production";
}
}
public class PropertyUtils {
public static Properties getProperties(String profile, String filename) throws IOException {
Properties ret = new Properties();
ClassPathResource resource;
resource = new ClassPathResource("properties/common/" + filename + ".properties");
ret.putAll(PropertiesLoaderUtils.loadProperties(resource));
resource = new ClassPathResource("properties/" + profile + "/" + filename + ".properties");
if (resource.exists()) {
ret.putAll(PropertiesLoaderUtils.loadProperties(resource));
}
return ret;
}
}
#Configuration
public class MainConfig {
private #Autowired String profile;
// Here you can use: PropertyUtils.getProperties(profile, "jdbc"))
}
This should get you started
<bean id="allProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="singleton" value="true"/>
<property name="ignoreResourceNotFound" value="true"/>
<property name="locations">
<list>
<value>classpath*:default.properties</value>
<value>classpath*:overrides.properties</value>
<value>file:${APP_HOME}/**/*.properties</value>
</list>
</property>
</bean>

Resources