I am using spring 3.0.5 and trying to read properties files to make some kind of validation as well as datasource. But i am getting null when i use #Value ,below are my cfg.
in applicationContext.xml
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:database.properties"/>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<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>// here It is perfectly establishing the data-source.
The Class where I want to exposed the values of properties file
#Component
public class PropertyReaderBean {
//#Value("#{propertyConfigurer1[dailyLimit]}")
//#Value("#{database['jdbc.driverClassName']}")
#Value("${jdbc.driverClassName}")// I tried all three but still getting null
private String limit;
public String getLimit()
{
System.out.println(" limit : "+limit);
return limit;
}
public void setLimit(String limit) {
System.out.println(" limit : "+this.limit);
this.limit = limit;
}
and finally the databse.properties file
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/imps
jdbc.username=root
jdbc.password=root
So whenever i am trying to access the values properties file using above configuration, i am getting null, please guide.
Update:
However the setter method of PropertyReaderBean is not working, i have checked the stacktrace, but when i add in xml like this then i can read the properties file values.
<bean id="propertyDao" class="com.alw.imps.validator.PropertyReaderBean">
<property name="limit" value="${jdbc.password}"></property>
</bean>
A likely cause is #Value doesn't work. Did you already declare < context:annotation-config /> in the application context?
You don't need property configurer, the definition of which in your code has syntax errors.
You can just do this and reference in code as you have :
<context:property-placeholder location="classpath:database.properties"/>
which relies on this
xmlns:context="http://www.springframework.org/schema/context"
and
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
Related
I'm developing an application that receives various XML files containing different 'Individual' objects that I store in a database.
The goal is that all the files are read and treated. If a file is processed correctly it moves to the "processed" folder. If an exception is thrown it moves to an error folder.
The desired behaviour is that if an error occurs in one of the files, everything is rollbacked , nothing is saved in the database and all the files are copied to the error folder (also the already processed ones)
The copying of the folders probably can't be done using transactions so I do them manually...
The structure of my project is the following:
Technologies:
Hibernate : 3.5.0-Final
Spring : 3.1.1.RELEASE
Server : Tomcat 7
Database : SQL Server
I start from the idea that the best location to put the transaction is the service. I don't add the propagation property, since I want the default Property.REQUIRED behaviour:
#Transactional(rollbackFor = Exception.class)
private Feedback readIndividuals(File fileLocation) throws Exception {
System.out.println("Start reading individuals");
//Set the status of all database entries to DELETED
individualEntityService.setAllStatussesToDeleted();
}
final File individualsProcessedFolder = new File(individualsProcessedFolderLocation);
for (final File fileEntry : fileLocation.listFiles()) {
if (fileEntry.isDirectory()) {
readIndividuals(fileEntry, feedback);
} else {
individualReader.read(fileEntry.getAbsolutePath());
....
Here I start the transaction. The individualReader is a service that performs the actual reading of the file and the writing to the DB.
EDIT
Here the code of the IndividualReader where I call the add method in the EntityService:
#Override
#Transactional
public void read(String fileLocation) throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(CDM.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
XMLInputFactory factory = XMLInputFactory.newInstance();
FileInputStream fileInputStream = new FileInputStream(fileLocation);
XMLStreamReader xmlr = factory.createXMLStreamReader(fileInputStream, ENCODING);
try {
xmlr.nextTag();
xmlr.require(XMLStreamConstants.START_ELEMENT, null, "CDM");
xmlr.nextTag();
while (xmlr.getEventType() == XMLStreamConstants.START_ELEMENT) {
JAXBElement<CDM.Individual> jaxbIndividual = unmarshaller.unmarshal(xmlr,
CDM.Individual.class);
CDM.Individual individual = jaxbIndividual.getValue();
Individual individualDO = individualBuilder.build(individual);
Set<Diploma> diplomas = diplomaBuilder.build(individual.getDiplomas(), individualDO);
Set<HealthCareProfessional> healthCareProfessionals = healthCareProfessionalBuilder.build(individual.getHCProfessionals());
individualDO.addHealthCareProfessionals(healthCareProfessionals);
individualDO.addDiplomas(diplomas);
LOG.debug("Adding individual with SSIN [" + individualDO.getSsin() + "] into DB");
Individual retrievedIndividual = individualEntityService.read(individualDO.getSsin());
if (retrievedIndividual != null) {
individualEntityService.remove(retrievedIndividual);
individualDO.setStatus(EntryStatus.UPDATED);
}
individualEntityService.add(individualDO);
LOG.debug("Individual with SSIN [" + individualDO.getSsin() + "] successfully added to DB");
LOG.debug(getIndividualXMLAsString(individualDO));
if (xmlr.getEventType() == XMLStreamConstants.CHARACTERS) {
xmlr.next();
}
}
} finally {
xmlr.close();
fileInputStream.close();
}
}
The lower level is the EntityService:
#Override
#Transactional
public void add(Individual individual) {
individualDao.addIndividual(individual);
}
This class doesn't do anything more than calling the DAO, I annotated it with the #Transactional annotation. Since the default is Propagation.REQUIRED it won't start a new physical transaction, but it will join the transaction of the service.
Finally we have the DAO:
#Transactional
public void addIndividual(Individual individual) {
em.persist(individual);
}
I also annotate this method with Transactional, with the same reason as above.
The Entity manager is autowired in the DAO using Spring:
#PersistenceContext
private EntityManager em;
The Entity Manager is defined in the applicationContext as follows:
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="individual"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="generateDdl" value="true"/>
<property name="showSql" value="false"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
</props>
</property>
<property name="dataSource" ref="dataSource"/>
</bean>
Now everything compiles and deploys fine and works even as expected. But when I make one of the XML files corrupt all the files before the corrupt file end up in the DB and the transaction is not rollbacked.
I guess I must be missing something and probably my mistake is in the wrong usage of the combination #Transaction and the Spring EntityManager. I never use the explicit em.flush() to push the data to the DB. Maybe the em.persist is wrong and stores the data to the database and I can't recover from it...
Anyone has an idea of what I'm doing wrong? Help would be highly appreciated.
EDIT Hereby the complete context:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:component-scan base-package="be.healthconnect.pwg" />
<task:annotation-driven />
<tx:annotation-driven />
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:/be/healthconnect/pwg/core/properties/pwg.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${datasource.driver.class.name}" />
<property name="url" value="${datasource.url}" />
<property name="username" value="${datasource.username}" />
<property name="password" value="${datasource.password}" />
</bean>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="individual"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="generateDdl" value="true"/>
<property name="showSql" value="false"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
</props>
</property>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="jpaVendorAdaptor"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>
</beans>
The mistake I made was the following: #Transactional annotation had no effect because it annotated a private method. The proxy generator was ignoring it.
I found the solution in the Spring Manual chapter 10.5.6:
Method visibility and #Transactional
When using proxies, you should apply the #Transactional annotation only to methods with public visibility.
If you do annotate protected, private or package-visible methods with the #Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.
AWS talks about System.getProperty("JDBC_CONNECTION_STRING") in http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_Java.managing.html after we set up our environmental variables. All great except I can't call System.getProperty inside my Spring XML configuration code nor I can call for resource bundle shortcuts since resources bundle itself has to extract somehow these environmental variables to serve them. Could you kindly help me please to convert this example config to use environmental variables ? :-)
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://secrethost:007/whois?autoReconnect=true" />
<property name="username" value="bond" />
<property name="password" value="abuginsidemistycorner" />
<property name="initialSize" value="100" />
<property name="minEvictableIdleTimeMillis">
<value>300000</value>
</property>
<property name="timeBetweenEvictionRunsMillis">
<value>60000</value>
</property>
<property name="maxIdle" value="20" />
</bean>
I was not able to understand what do people do here:
Can I use an Environment variable based location for Spring FileSystemResource? which would work for recent spring version?
First add a <context:property-placeholder .. /> element to your configuration.
<context:property-placeholder />
Then simply use placeholders in your config.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${JDBC_CONNECTION_STRING}" />
<property name="username" value="bond" />
<property name="password" value="abuginsidemistycorner" />
<property name="initialSize" value="100" />
<property name="minEvictableIdleTimeMillis" value="30000" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="maxIdle" value="20" />
</bean>
Make sure that the placeholder names match your variables you have setup.
If you are using the class org.springframework.beans.factory.config.PropertyPlaceholderConfigurer to load the property files, you can set the property systemPropertiesMode to the value SYSTEM_PROPERTIES_MODE_OVERRIDE.
In the spring.xml you'll have this bean:
<bean id="propertyPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="locations">
<list>
<value>classpath://file.properties</value>
</list>
</property>
</bean>
Spring will load the system properties in this way:
Check system properties first, before trying the specified properties.
This allows system properties to override any other property source.
In this way you should be able to read the system properties as normal properties.
For someone who use JavaConfig:
In #Configuration file we need to have:
#Bean
public static PropertyPlaceholderConfigurer properties() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ClassPathResource[] resources = new ClassPathResource[ ] {
new ClassPathResource("db.properties")
};
ppc.setLocations( resources );
ppc.setIgnoreUnresolvablePlaceholders( true );
ppc.setSearchSystemEnvironment(true);
return ppc;
}
#Value("${db.url}")
private String dbUrl;
#Value("${db.driver}")
private String dbDriver;
#Value("${db.username}")
private String dbUsername;
#Value("${db.password}")
private String dbPassword;
#Bean
public DataSource db() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl(dbUrl);
dataSource.setDriverClassName(dbDriver);
dataSource.setUsername(dbUsername);
dataSource.setPassword(dbPassword);
return dataSource;
}
important is line: ppc.setSearchSystemEnvironment(true);
after that in db.properties, I have:
db.url = ${PG_URL}
db.driver = ${PG_DRIVER}
db.username = ${PG_USERNAME}
db.password = ${PG_PASSWORD}
I am new to Spring and I have a small requirement.
<bean name="triangle" class="com.thomson.learn.spring.triangle">
<property name="name" ref="dataSource" />
</bean>
<bean id="dataSource" class="java.lang.String">
<property name="name" value="easy" />
</bean>
I have written beans as shown above. I need to access the value easy from the bean with id="triangle", but when I try doing this I get a exception. Can someone please suggest how to overcome this problem?
Try the following:
<bean name="triangle" class="com.thomson.learn.spring.triangle">
<property name="name" ref="dataSource" />
</bean>
<bean id="dataSource" class="com.thomson.learn.spring.shape">
<property name="name" value="easy" />
</bean>
where com.thomson.learn.spring.triangle and com.thomson.learn.spring.shape must exist as classes in your project. your String class bean couldn't have worked because String has no name property (getName() method getter) to access.
If it is going to be String(s) that you want to inject into all of your beans you can refer properties from a property file and use <context:property-placeholder location="location to .properties"/>
or you can define properties in the configuration xml with <util:properties />
Example for property-placeholder
<bean id="appDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/> // Refered from property file
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="classpath:jdbc.properties"/>
A String does not have a property 'name', so you can't instantiate one the way you are trying to. Moreover, you don't need to, as you can use it directly as the value, e.g.:
<bean name="triangle" class="com.thomson.learn.spring.triangle">
<property name="name" value="easy" />
</bean>
Edit in reply to your comment:
If "name" is of type shape, then you need to inject a shape here...
<bean name="triangle" class="com.thomson.learn.spring.triangle">
<property name="name" ref="shape" />
</bean>
<bean name="shape" class="com.thomson.learn.spring.shape">
<!-- configure properties here -->
</bean>
The objects wired by Spring are supposed to be JavaBeans. What this means in practice is that they have a no-argument constructor and a set of properties, each of which has both a getter and a setter. When you write a definition such as above, Spring uses the setters to set properties you defined in the configuration file.
For instance if Shape was a class as such:
package com.thomson.learn.spring.Shape;
class Shape {
private String name;
public Shape() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Then you could wire it with the following:
<bean id="myShape" class="com.thomson.learn.spring.Shape">
<property name="name" value="blah" />
</bean>
Which really translates to Spring executing the following at runtime:
Shape myShape = new Shape();
shape.setName("blah");
That's all there is to it, except that properties can also be more complex instances of other classes of course, which is why you can use ref to refer to another bean for a property.
P.S.: Spring can use reflection to inject values in many cases, so it doesn't actually need a setter, but most people implement their beans this way for various reasons.
I have multiple property files, a general one called application.properties and a more specific on called [HostName].properties
The Hostname.properties is set to higher priority than application.properties.
In application.properties I set the db.url:
db.url=jdbc:mysql://${db.host}:3306/${db.schema}?autoReconnect=true&sessionVariables=storage_engine=InnoDB;
I also set the db.host and db.schema values in this properties file.
db.schema=default
db.host=localhost
Then in [Hostname].properties I want to override the values for db.host and db.schema:
db.schema=specific
db.host=specific_hostname
However, it seems as if this nesting is not working.
The db.schema value resolves to "specific" but the db.url property resolves to the db.schema specified in the same file as the db.url is defined in.
Consider the following test snippet:
#Value("${db.url}")
private String dbUrl;
#Value("${db.schema}")
private String dbSchema;
#Test
public void testUrlTest() {
System.out.println(dbUrl);
System.out.println(dbSchema);
}
First value printed out is
jdbc:mysql://localhost:3306/default?autoReconnect=true&sessionVariables=storage_engine=InnoDB;
Second value is:
specific
Is this by design? I would love to be able to override nesting from one property configurer to the next.
Is there some way around this?
UPDATE:
The Property place holders are configured in the spring config xml as follows:
<context:property-placeholder order="10"
location="classpath:xxx.yyy/application.properties"
ignore-unresolvable="true" ignore-resource-not-found="false" />
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" ref="hostnameLookup" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="5" />
</bean>
<bean class="xxx.yyy.util.HostNameLookupFactory" id="hostnameLookup"
autowire-candidate="false">
<property name="prefix" value="classpath:xxx.yyy/" />
<property name="suffix" value=".properties" />
</bean>
The HostNameLookupFactory is a factory bean that provides the local host name.
Is it possible to define a datasource connector in a Spring controller ?
I'm working on a tool : synchronize a source table to a target table.
I would define source and target in my controller (to synchronize different databases - in my view I can select different source and target databases).
Actually, I define my datasource in file call : datasource.xml
My code :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config />
<bean id="sourceDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/source"/>
<!--<property name="url" value="jdbc:mysql://linkSource"/>-->
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="targetDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/target"/>
<!--<property name="url" value="jdbc:mysql://linkTarget"/>-->
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
</beans>
Thank you for your help !
Thank you for your help !
But I think I put my question badly.
Actually, I have in my sync-servelt.xml (just part) :
<!--sync query beans-->
<bean id="sourceDatasetQueryBean" class="ds.sync.db.SyncDatasetQuery" name="sourceDatasetsQuery">
<property name="dataSource" ref="sourceDataSource"/>
</bean>
<bean id="targetDatasetQueryBean" class="ds.sync.db.SyncDatasetQuery" name="targetDatasetsQuery">
<property name="dataSource" ref="targetDataSource"/>
</bean>
<bean id="sourceDatasetDescriptionQueryBean" class="ds.sync.db.SyncDatasetDescriptionQuery" name="sourceDatasetsDescriptionQuery">
<property name="dataSource" ref="sourceDataSource"/>
</bean>
<bean id="targetDatasetDescriptionQueryBean" class="ds.sync.db.SyncDatasetDescriptionQuery" name="targetDatasetsDescriptionQuery">
<property name="dataSource" ref="targetDataSource"/>
</bean>
...more...
And, in my controller I'm using :
#Autowired
#Qualifier("sourceDatasetQueryBean")
protected SyncDatasetQuery m_datasetQuerySource;
#Autowired
#Qualifier("targetDatasetQueryBean")
protected SyncDatasetQuery m_datasetQueryTarget;
#Autowired
#Qualifier("sourceDatasetDescriptionQueryBean")
protected SyncDatasetDescriptionQuery m_datasetDescriptionQuerySource;
#Autowired
#Qualifier("targetDatasetDescriptionQueryBean")
protected SyncDatasetDescriptionQuery m_datasetDescriptionQueryTarget;
...more...
I have 11 tables to sync between source and target...
Is there a way to group my query beans ?
My synchronizations must be performed on several databases.
For example, I have 3 sites in different places, 1 site is SOURCE (A), 2 sites are TARGET (B & C) ; with a form (made with YUI), I should be able to sync A->B and A->C.
To sum up :
1- with my form I select a SOURCE, and a TARGET (serveral databases),
2- my form send (in Ajax), the selected SOURCE and selected TARGET to my controller,
3- my controller points to the good database.
What is the best way to do this ?
Using a Factory ?
Using setDataSource ?
Thank you for help.
You should be able to use the following syntax to achieve what you want (see Spring 2.x docs):
#Autowired
#Qualifier("targetDataSource")
DataSource targetDataSource;
#Autowired
#Qualifier("sourceDataSource")
DataSource sourceDataSource;
So assuming your data sources are defined correctly it's only a matter of injecting them into your Controller:
<bean id="myController" class="...">
<property name="sourceDS" ref="sourceDataSource" />
<property name="targetDS" ref="targetDataSource" />
....
</bean>
If you don't want to be messing with spring xml files, and relay in properties or any other GUI to define those datasources at runtime, you might use:
applicationContext.getBean(bean,object[])
Be aware that is not a good practise with spring (even that it is quite handy sometimes).
This way you define your beans expecting constructor arguments and supply those arguments as part of the array. This way you create as many datasources you need at runtime getting those from wherever you want to store the information.
Finally, by using DriverManagerDataSource, and using setter, I can redefine my dataSource selected (target and source) dynamically in my controller.
I just need to use :
setDriverManagerDataSource(m_sourceDataSource);
and m_datasetQuerySource.setDataSource(dataSource); (SOURCE)
Same play with target and all tables.
I see also other way to do that :
http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/
http://grails.org/Spring+Bean+Builder