How do I read JVM arguments in the Spring applicationContext.xml - java

I have a JSF web application with Spring and I am trying to figure out a way to reference the JVM arguments from the applicationContext.xml. I am starting the JVM with an environment argument (-Denv=development, for example). I have found and tried a few different approaches including:
<bean id="myBean" class="com.foo.bar.myClass">
<property name="environment">
<value>${environment}</value>
</property>
</bean>
But, when the setter method is invoked in MyClass, the string "${environment}" is passed, instead of "development". I have a work around in place to use System.getProperty(), but it would be nicer, and cleaner, to be able to set these values via Spring. Is there any way to do this?
Edit:
What I should have mentioned before is that I am loading properties from my database using a JDBC connection. This seems to add complexity, because when I add a property placeholder to my configuration, the properties loaded from the database are overridden by the property placeholder. I'm not sure if it's order-dependent or something. It's like I can do one or the other, but not both.
Edit:
I'm currently loading the properties using the following configuration:
<bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc.mydb.myschema"/>
</bean>
<bean id="props" class="com.foo.bar.JdbcPropertiesFactoryBean">
<property name="jdbcTemplate">
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="myDataSource" />
</bean>
</property>
</bean>
<context:property-placeholder properties-ref="props" />

You can use Spring EL expressions, then it is #{systemProperties.test} for -Dtest="hallo welt"
In your case it should be:
<bean id="myBean" class="com.foo.bar.myClass">
<property name="environment">
<value>#{systemProperties.environment}</value>
</property>
</bean>
The # instead of $ is no mistake!
$ would refer to place holders, while # refers to beans, and systemProperties is a bean.
May it is only a spelling error, but may it is the cause for your problem: In the example for your command line statement you name the variable env
(-Denv=development, for example...
But in the spring configuration you name it environment. But both must be equals of course!

If you register a PropertyPlaceholderConfigurer it will use system properties as a fallback.
For example, add
<context:property-placeholder/>
to your configuration. Then you can use ${environment} in either your XML configuration or in #Value annotations.

You can load a property file based on system property env like this:
<bean id="applicationProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="false" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="searchSystemEnvironment" value="false" />
<property name="locations">
<list>
<value>classpath:myapp-${env:prod}.properties</value>
</list>
</property>
</bean>
If env is not set default it to production otherwise development and testing teams can have their flavor of app by setting -Denv=development or -Denv=testing accordingly.

Use #{systemProperties['env']}. Basically pass the propertyName used in the Java command line as -DpropertyName=value. In this case it was -Denv=development so used env.

Interestingly, Spring has evolved to handled this need more gracefully with PropertySources:
http://spring.io/blog/2011/02/15/spring-3-1-m1-unified-property-management/
With a few configurations and perhaps a custom ApplicationInitializer if you are working on a Web app, you can have the property placeholder handle System, Environment, and custom properties. Spring provides PropertySourcesPlaceholderConfigurer which is used when you have in your Spring config. That one will look for properties in your properties files, then System, and then finally Environment.

Spring 3.0.7
<context:property-placeholder location="classpath:${env:config-prd.properties}" />
And at runtime set:
-Denv=config-dev.properties
If not set "env" will use default "config-prd.properties".

Related

Spring : Initialization of properties before any bean creation

I have the project structure as following -
Facade -> Service-> DAO
In the DAO layer, when the beans are initialized then many dependencies are injected from a property file. Therefore, the properties file must be read first and then the remaining dao beans must be created. When the application is started then it gives an error that Spring cannot resolve a placeholder.
The DAO-application-context.xml is like-
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="prop">
<value>app.properties</value>
</property>
</bean>
<import resource = "a-dao.xml" />
<import resource = "b-dao.xml" />
<import resource = "c-dao.xml" />
Now in all the child application contexts i.e. a-dao, etc, we have-
<bean ....>
<property name = "xyz">
<value>${appValue}<value/>
</property>
<bean>
The error received is that appValue cannot be resolved. I think that it may be due to incorrect sequence of bean creation. However, the same config is working in another larger project.
I have checked Order of Spring Bean Initialization but implementing that solution would not be feasible. Is there any other way ?
Reg this Block of Configuration, property prop seems to be wrong
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="prop">
<value>app.properties</value>
</property>
</bean>
According to the Spring documentation
You could use the property location or locations to set the one or multiple values of the properties file.
So the code should be refactored to
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>app.properties</value>
</property>
</bean>

Does Spring supports nested SpEL expressions?

This is my properties file:
base1.jdbc.password=pass1
base2.jdbc.password=pass2
base3.jdbc.password=pass3.
I have an environment variable called %DATABASE% which can be either base1 or base2 or base3.
How can i read the password property of the database stored in the environment variable? i thought about something like:
<property name="password" value="${#{systemProperties['DATABASE']}.jdbc.password}"/>.
but not sure if it's correct.
Add property place holder
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:xxxxx.properties</value>
</list>
</property>
</bean>
Then
<bean id="bean" class="xxx.class">
<property name="password" value="#{systemProperties['DATABASE']}.jdbc.password"/>
</bean>
Use the following configuration in your application context xml
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>##YOUR PROPERTIES FILE NAME###</value>
</property>
</bean>
You can get the details from an associated question (How do you configure a Spring bean container to load a Java property file?)
The description of the PropertyPlaceholderConfigurer is given here(http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html)
Use the name of the properties file that you have in the <value> tag
Following is an example of how I have used the properties in my sample project. I have a bean called processRetryPolicy with the properties with are loaded from the properties file.
<bean id='processRetryPolicy' class='com.poc.process.model.ProcessRetryPolicy' >
<property name="isActive" value="${process.executor.conn.retry.policy.isActive}"/>
<property name="intervalType" value="${process.executor.conn.retry.interval.type}"/>
<property name="intervalvalue" value="${process.executor.conn.retry.interval.value}"/>
<property name="retryPolicy" value="${process.executor.conn.retry.default.policy}"/>
</bean>
The properties are defined in the properties file as
process.executor.conn.retry.policy.isActive = true
process.executor.conn.retry.interval.type = HOUR
process.executor.conn.retry.interval.value = 1
process.executor.conn.retry.default.policy = Retry
To add the environment properties I had used the following in one of the spring batch applications:-
<property name="environment" value="#{jobParameters['env.type']}"/>
The environment type was being passed as a runtime parameter as follows
java -Xmx12288m -D<<List of Params and Values>> env.type=$env
Another way of doing it in spring core is using the spring expression language (http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/expressions.html)
Your expression looks correct.

How to handle Spring application configuration?

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

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

Can I use an Environment variable based location for Spring FileSystemResource?

I have a requirement to have all our properties files be stored in a directory. The location of this directory should be stored in a system environment variable. In my application context I will need to access this environment variable to create the FileSystemResource bean. Here is an example of what I would normally have:
<bean id="properties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<bean class="org.springframework.core.io.FileSystemResource">
<constructor-arg>
<value>myprops.properties</value>
</constructor-arg>
</bean>
</property>
</bean>
Instead I will need to have it be something like
<value>${prop_file_location}/myprops.properties</value>
Where prop file location is an environment variable. Does anyone know an easy way of doing this?
I am using spring 2.5.6 and java 1.6
UPDATE
We later upgraded to Spring 3.0.X and we were able to take advantage of the spring expression language. Our approach simplified from three beans to the following snippet:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:defaults.properties</value>
<value>file:/a/defined/location/project.properties</value>
<value>file:${AN_ENV_CONFIGURED_DIR}/project.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
<property name="searchSystemEnvironment" value="true" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
</bean>
This allowed us to have either a development (the first defaults) statically well known location, or a deployed location configured via env variables. The configurer processes these in order (i.e. the deployed takes precedence over the defaults).
OLD
I ended up going with a non programmatic approach. I used a MethodInvoker to retrieve the environment value. I was able to then pass that into the FileSystemResource.
<bean id="configPath" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" >
<property name="targetClass" value="java.lang.String" />
<property name="staticMethod" value="java.lang.System.getenv" />
<property name="arguments">
<list>
<value>NAME_OF_VARIABLE</value>
</list>
</property>
</bean>
while I note that the question required 2.5.x, it's worth pointing out that Spring 3's EL support (available since Nov 2009) would've made short work of this sort of thing
#Value("#{ systemProperties['user.home'] }")
private String userHome ;
or
<property name = "userHome" value ="#{ systemProperties['user.home'] }"/>
You could always extend the FileSystemResource (i.e. PropertiesFileResource) that would initialize itself by taking prepending the property file location system property to the file path.
In Spring.Net we have got the IVariableSource interface and PropertyPlaceholderConfigurer which are able to retrieve values from environment variables. Maybe there is something similar in the Spring Framework for Java?
Edit: I think I found the corresponding java bean which is named PropertyPlaceholderConfigurer as well in the java docs.
Example:
Start you app with -DDA_HOME=c:\temp
c:\temp must contain directory called "config"
In c:\temp\config you have file app.properties (for example)
Extend the Spring loader:
public class Loader extends org.springframework.beans.factory.config.PropertyPlaceholderConfigurer{
private String appHome;
public void setAppHome(String appHome) {
this.appHome = appHome;
}
#Override
public void setLocation(Resource location) {
if(appHome==null){
throw new RuntimeException("You must specify VM property DA_HOME, this directory must contain " +
"another directory, called config, inside the config directory app.properties " +
"file must be found with the configuration properties");
}
String configurationDirectory = appHome + System.getProperty("file.separator") + "config";
String fileName = location.getFilename();
Resource file = new FileSystemResource( configurationDirectory + System.getProperty("file.separator")+ fileName);
super.setLocation(file);
}
}
Specify the new loader and its configuration base:
<bean id="placeholderConfig" class="your.Loader">
<property name="appHome" value="#{systemProperties['DA_HOME']}"/>
<property name="location" value="app.properties" />
</bean>

Categories