How to use environment variables with properties file in Spring context file - java

I am trying to read in a configuration file based on a system environment variable. My environment variable is FOO_ENV with value dev and dev.properties contains the properties bar.host and bar.port.
<context:property-placeholder />
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:${FOO_ENV}.properties"></property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
<bean id="myServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
<property name="ignoreUnresolvablePlaceholders" value="true" />
<constructor-arg type="String" value="http://${my.host}:${my.port}/" />
</bean>
When I deploy this in tomcat, I get the following error:
11:48:39.324 [localhost-startStop-14] ERROR o.s.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'myServer' defined in ServletContext resource [/WEB-INF/my-context.xml]:
Could not resolve placeholder 'my.host' in string value [http://${my.host}:${my.port}]
at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:209) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE]
By replacing $FOO_ENV with dev in the context file, I have determined that the properties file can be read correctly. By changing FOO_ENV to other names, I can show that Spring is reading the environment variable.
It seems that the element
<property name="ignoreUnresolvablePlaceholders" value="true" />
should allow Spring to ignore that ${my.host} is not an environment variable, but though I've tried it in various places, I still get the same error, which indicates that my.host is not found.

You actually have two PropertyPlaceHolderConfigurers defined here. One via the context namespace and one explicitly. Spring is probably picking the one created via the context namespace. You could either set 'ignore-unresolvable' on the context tag and remove your propertyConfigurer bean like so:
<context:property-placeholder ignore-unresolvable="true"/>
Or if you need more control over PropertyPlaceHolderConfigurer go the other way and remove the context:property-placeholder tag.

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.

Spring common property reference with different value per application

I have a bean which is used across all projects (different war files).
That particular bean requires a property appname (to know which app is using the bean).
How can i configure this?
I tried passing the value in the following way:
<bean id="appNameProperty" class="java.util.Properties">
<property name="appName" value="app1" />
</bean>
Bean definition:
<bean id="someClass" class="someClass">
<property name="appName" value="#{appNameProperty.appName}" />
</bean>
Where appName is supposed to be a String value.
I get the following exception while deploying my app:
Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'appName' of bean class [java.util.Properties]: Bean property 'appName' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
To avoid the VM arguments you could use the property placeholder configurer as mentioned above. If the app name is fixed for every application and you are using war-files everywhere you could add e.g. "/WEB-INF/config" to the list of the properties location and place there a properties file containing the app-name:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- further locations -->
<value>WEB-INF/config/*.properties</value>
<!-- further locations -->
</list>
</property>
<!-- further configuration as needed -->
</bean>
This may help you.
<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="yourBean" class="path.to.your.BeanClass">
<property name="appName" value="#{systemProperties['appName']}"/>
</bean>
All you need to do is pass VM arguement -DappName=abcApp while starting the app.
ref PropertyPlaceHolderConfigurer

How to load data from property file into bean property values?

I am following the following article.
http://www.mkyong.com/spring/spring-quartz-scheduler-example/
Everything works fine.
<bean id="simpleTrigger"
class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="runMeJob" />
<property name="repeatInterval" value="5000" />
<property name="startDelay" value="1000" />
</bean>
I created a property file app.properties which has
repeatInterval = 5000
startDelay = 1000
I want to load these data into bean properties. Right now I have to hard code the values into the xml file.
I want to be able to load the data from property file into the bean properties. Is it possible?
EDIT:
I have
<property name="repeatInterval" value="5000" />
What I am looking for is a way to make it
<property name="repeatInterval" value= "get 5000 from property file" />
To find a file myPropertyFileName.properties that is on your classpath and load it into your spring config, create the following bean:
<bean id="myPropertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:myPropertyFileName.properties"/>
<property name="placeholderPrefix" value="${props:"/>
</bean>
Then use a property name defined like
repeatInterval=5000
like this:
<property name="repeatInterval" value="${props:repeatInterval}"/>
Use Spring propertyPlaceholderConfigurer to achieve this. Follow this guide.
I have run into something similar in the past. I needed to load a bunch of beans using Spring but I wanted them to be user editable bean files. So I didn't want to include them in the jar packaging. What I did was create my user-defined bean files outside the jar but in a know relative location. My packaged bean definition file referenced the beans defined in the user-defined bean file and when I loaded the application context I provided both files (user-defined & packaged).
Bit unorthodox but it worked.

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

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

Categories