How to handle Spring application configuration? - java

I have standalone Spring application that has its setting in DMBS. I have an idea to use only one code (that specifies particular instance) when application is starting and application reads own setting from database. Setting values are then used for creating beans in applicationContext XML file and later in beans itself.
So far (developing phase) I used one properties file and read it in such way:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/taskproducer.properties</value>
</list>
</property>
</bean>
What is the best way how to handle setting from database in Spring application?

You are heading the right way. Property files should indeed contain the database configuration properties.
If you want to go one-step further, you can have profile-specific property files, e.g. development-specific configuration and production-specific.
Take a look at this video for some nice instructions on this subject.
EDIT: in case I misunderstood, and you wanted some guidance on how to setup your database using these properties, here is an example of a Spring XML configuration, based on properties from a configuration file.
Short mention: for example, you would setup your DataSource like this:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</bean>
Then in your property file you would have defined the following properties:
database.url=http://localhost:3306/mydb
database.username=sa
database.password=
Hope this is helpful

You can create a configurer bean which reads props from DB
class DbProperties extends java.util.Properties {
DbProperties() {
String v1 = ... // read prop from db
setProperty("p1", "v1");
}
}
add it to Spring context
...
<context:annotation-config />
<bean id="c1" class="DbProperties" />
<bean id="b1" class="B1"/>
<!--
<context:property-placeholder location="taskproducer.properties" />
-->
<context:property-placeholder properties-ref="c1"/>
and use it
class B1 {
#Value("${p1}")
int x;
}

Related

Can not update config file using PropertyPlaceholderConfigurer

Could it be possible to update property using PropertyPlaceholderConfigurer
applicationContext.html
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="false"></property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations" value="classpath:config.properties" />
</bean>
<bean id="appConfig" class="com.abc.Configuration">
<property name="myProperty" value="${config.request.myProperty}" />
</bean>
Configuration.java
#Configuration
#Component
public class ServerConfig {
private int myProperty;
public int getMyProperty(){return myProperty;}
public int setMyProperty(int value){this.myProperty = value }
}
config.properties
myProperty=123456
I can get "myProperty" value using getMyproperty(). But i can not update property using setMyProperty() - The config file was not update, so new value will be lost if application was restart.
Any help would be much appreciated.
Updating properties like that will not work. You need to access the configuration file directly and write your changes to it. The problem is that PropertyPlaceholderConfigurer may read from different types of sources (in your case classpath resource). Some of them may be read-only.
Also keep in mind that event if you make changes they will not be automatically picked up. You'll have to refresh the spring context. Or you can use some dedicated configuration library (e.g. cfg4j)

Is there a way to have if/else conditions in an xml in spring mvc3?

I have a properties file defined in my xml:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/db.properties"></property>
</bean>
I have a property in the file:
someprop = one
Question
In my XML I would like to either add/remove a property in a bean definition. For example:
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.internal.url}" />
<!--I want to add/remove the line below based on value in property file-->
<property name="username" value="${jdbc.internal.username}" />
</bean>
This might be possible using SpringEL. Read the Expression support for defining bean definitions section here.
If this is to handle multiple environments then Spring 3.1 provides bean definition profiles and Environment abstractions.

How to fetch value of the Datasource in spring frarmework using JAVA code?

I have spring application and using property file want to read the values from the PropertyPlaceholderConfigurer. Here datasource is given as Id.
I want to read the values of the datasource property jdbc.driverClassName value using java code.
Scenario should be: 1st bean will be executed. It will load the data from the jdbc.property file. All the values in the datasource should be read from the java code.
How to read the values from the Java code for PropertyPlaceholderConfigurer(datasource)?
Given the executed scenario below :
Create a properties file (database.properties), include your database details, put it into your project class path.
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mkyongjava
jdbc.username=root
jdbc.password=password
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>database.properties</value>
</property>
</bean>
<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>
Please help me out using java code to read the data from the given datasource?
Or any useful link from where i can find out the resolution.
There are two simple options:
Get a reference to the application context (if you instantiated it yourself, that should be easy- if you initialized it using say web.xml, you might be able to retrieve it using org.springframework.web.context.support.WebApplicationContextUtils). Then obtain the bean using BeanContext<T>.T getBean(String name, Class<T> requiredType), retrieve the properties using DriverManagerDataSource's getters.
Inject the same values into one of your own beans; Spring will put the same values for you

how to read System environment variable in Spring applicationContext

How to read the system environment variable in the application context?
I want something like :
<util:properties id="dbProperties"
location="classpath:config_DEV/db.properties" />
or
<util:properties id="dbProperties"
location="classpath:config_QA/db.properties" />
depending on the environement.
Can I have something like this in my application Context?
<util:properties id="dbProperties"
location="classpath:config_${systemProperties.env}/db.properties" />
where the actual val is set based on the SYSTEM ENVIRONMENT VARIABLE
I'm using Spring 3.0
You are close :o)
Spring 3.0 adds Spring Expression Language.
You can use
<util:properties id="dbProperties"
location="classpath:config_#{systemProperties['env']}/db.properties" />
Combined with java ... -Denv=QA should solve your problem.
Note also a comment by #yiling:
In order to access system environment variable, that is OS level
variables as amoe commented, we can simply use "systemEnvironment"
instead of "systemProperties" in that EL. Like
#{systemEnvironment['ENV_VARIABLE_NAME']}
Nowadays you can put
#Autowired
private Environment environment;
in your #Component, #Bean, etc., and then access the properties through the Environment class:
environment.getProperty("myProp");
For a single property in a #Bean
#Value("${my.another.property:123}") // value after ':' is the default
Integer property;
Another way are the handy #ConfigurationProperties beans:
#ConfigurationProperties(prefix="my.properties.prefix")
public class MyProperties {
// value from my.properties.prefix.myProperty will be bound to this variable
String myProperty;
// and this will even throw a startup exception if the property is not found
#javax.validation.constraints.NotNull
String myRequiredProperty;
//getters
}
#Component
public class MyOtherBean {
#Autowired
MyProperties myProperties;
}
Note: Just remember to restart eclipse after setting a new environment variable
Check this article. It gives you several ways to do this, via the PropertyPlaceholderConfigurer which supports external properties (via the systemPropertiesMode property).
Yes, you can do <property name="defaultLocale" value="#{ systemProperties['user.region']}"/> for instance.
The variable systemProperties is predefined, see 6.4.1 XML based configuration.
In your bean definition, make sure to include "searchSystemEnvironment" and set it to "true". And if you're using it to build a path to a file, specify it as a file:/// url.
So for example, if you have a config file located in
/testapp/config/my.app.config.properties
then set an environment variable like so:
MY_ENV_VAR_PATH=/testapp/config
and your app can load the file using a bean definition like this:
e.g.
<bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchSystemEnvironment" value="true" />
<property name="searchContextAttributes" value="true" />
<property name="contextOverride" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>file:///${MY_ENV_VAR_PATH}/my.app.config.properties</value>
</list>
</property>
</bean>
Using Spring EL you can eis example write as follows
<bean id="myBean" class="path.to.my.BeanClass">
<!-- can be overridden with -Dtest.target.host=http://whatever.com -->
<constructor-arg value="#{systemProperties['test.target.host'] ?: 'http://localhost:18888'}"/>
</bean>
For my use case, I needed to access just the system properties, but provide default values in case they are undefined.
This is how you do it:
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchSystemEnvironment" value="true" />
</bean>
<bean id="myBean" class="path.to.my.BeanClass">
<!-- can be overridden with -Dtest.target.host=http://whatever.com -->
<constructor-arg value="${test.target.host:http://localhost:18888}"/>
</bean>
Declare the property place holder as follows
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations">
<list>
<value>file:///path.to.your.app.config.properties</value>
</list>
</property>
</bean>
Then lets say you want to read System.property("java.io.tmpdir") for your Tomcat bean or any bean then add following in your properties file:
tomcat.tmp.dir=${java.io.tmpdir}
This is how you do it:
<bean id="systemPrereqs" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" scope="prototype">
<property name="targetObject" value="#{#systemProperties}" />
<property name="targetMethod" value="putAll" />
<property name="arguments">
<util:properties>
<prop key="deployment.env">dev</prop>
</util:properties>
</property>
</bean>
But remember spring gets loaded first and then it will load this bean MethodInvokingFactoryBean. So if you are trying to use this for your test case then make sure that you use depends-on. For e.g. in this case
In case you are using it for your main class better to set this property using your pom.xml as
<systemProperty>
<name>deployment.env</name>
<value>dev</value>
</systemProperty>
You can mention your variable attributes in a property file and define environment specific property files like local.properties, production.propertied etc.
Now based on the environment, one of these property file can be read in one the listeners invoked at startup, like the ServletContextListener.
The property file will contain the the environment specific values for various keys.
Sample "local.propeties"
db.logsDataSource.url=jdbc:mysql://localhost:3306/logs
db.logsDataSource.username=root
db.logsDataSource.password=root
db.dataSource.url=jdbc:mysql://localhost:3306/main
db.dataSource.username=root
db.dataSource.password=root
Sample "production.properties"
db.logsDataSource.url=jdbc:mariadb://111.111.111.111:3306/logs
db.logsDataSource.username=admin
db.logsDataSource.password=xyzqer
db.dataSource.url=jdbc:mysql://111.111.111.111:3306/carsinfo
db.dataSource.username=admin
db.dataSource.password=safasf#mn
For using these properties file, you can make use of REsource as mentioned below
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource("classpath:"+System.getenv("SERVER_TYPE")+"DB.properties");
configurer.setLocation(resource);
configurer.postProcessBeanFactory(beanFactory);
SERVER_TYPE can be defined as the environment variable with appropriate values for local and production environment.
With these changes the appplicationContext.xml will have the following changes
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${db.dataSource.url}" />
<property name="username" value="${db.dataSource.username}" />
<property name="password" value="${db.dataSource.password}" />
Hope this helps .
Thanks to #Yiling. That was a hint.
<bean id="propertyConfigurer"
class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchSystemEnvironment" value="true" />
<property name="locations">
<list>
<value>file:#{systemEnvironment['FILE_PATH']}/first.properties</value>
<value>file:#{systemEnvironment['FILE_PATH']}/second.properties</value>
<value>file:#{systemEnvironment['FILE_PATH']}/third.properties</value>
</list>
</property>
</bean>
After this, you should have one environment variable named 'FILE_PATH'. Make sure you restart your terminal/IDE after creating that environment variable.
Updated version (2020).
Use System.getenv("ENV_VARIABLE")

How to pass a HashMap of properties to an Autowired bean in Spring?

Instead of instantiating a PersistenceManagerFactory within my app like this:
Properties properties = new Properties();
properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",
"org.datanucleus.jdo.JDOPersistenceManagerFactory");
properties.setProperty("javax.jdo.option.ConnectionDriverName","com.mysql.jdbc.Driver");
properties.setProperty("javax.jdo.option.ConnectionURL","jdbc:mysql://localhost/myDB");
properties.setProperty("javax.jdo.option.ConnectionUserName","login");
properties.setProperty("javax.jdo.option.ConnectionPassword","password");
PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(properties);
I want to configure it for dependency injection in Spring something like this:
<bean id="persistenceManagerFactory" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean" lazy-init="true">
<property name="persistenceManagerFactoryName" value="transactions-optional" />
</bean>
But I'm not sure how to pass the Properties in the application-context.xml (without using a jdoconfig.xml).
Is it possible in the application-context.xml to pass all of these Properties values for Autowiring?
The simple way to do this is to use the <props> element to specify the Properties object and its entries. This is described in Section 3.4.2.4 of the Spring Reference Manual.
There are other alternatives for more complicated use-cases; e.g. there's a properties factory class that can assemble a Properties object from multiple sources.
Either use an inline <props> block as Stephen C suggested, or use a properties file together with a PropertiesPlaceHolderConfigurer as suggested in the Spring Online Reference:
First register the PropertyPlaceholderConfigurer:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations"
value="classpath:com/foo/jdbc.properties"/>
</bean>
Or use the new-school equivalent shortcut:
<context:property-placeholder
location="classpath:com/foo/jdbc.properties"/>
This is sample content for the properties file:
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
And here's how you assign the properties:
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<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>
Examples are taken from the Spring Reference. The nice thing about this approach is that a) you can reuse the same properties file for different application contexts, b) for unit tests, you just put a different version of the properties on the classpath (in a maven scenario in src/test/resources instead of src/main/resources) and you don't have to change anything else.
You can also configure the LocalPersistenceManagerFactoryBean directly with a properties file (see PersistenceManagerFactory setup):
<beans>
<bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">
<property name="configLocation" value="classpath:kodo.properties"/>
</bean>
</beans>

Categories