How to inject properties into a spring bean from Main class - java

I'm using spring with my application, and I'm able to inject some properties from some file on a class path into my app and everything works perfectly. i.e.
<bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchContextAttributes" value="true" />
<property name="contextOverride" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:application.properties</value>
</list>
</property>
</bean>
Now I can use ${any.property.from.application.properties} in my spring context. And in my main class :
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("appContext.xml");
It works as well, my question is how do I inject property file location in the spring context without it being there at first, I want to make my app configurable. If I'm executing my app from C:\dir or /user/home/dir I assume that in the application context the value should be either C:\application.properties or /user/home/dir/application.properties

I had a similar problem sometime back. My requirement was the the property files is not bundled inside the application (and hence not in classpath). The file could be at any location in file system.
Here is how I solved it,
Define an environment variable the value of which points to the location of application.properties.
Lets say we have a env variable APP_PROP_HOME with value /user/home/dir/
Now while defining ServletContextPropertyPlaceholderConfigurer, define the locations as follows
I am reusing your example
<bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchContextAttributes" value="true" />
<property name="contextOverride" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>file://${APP_PROP_HOME}/application.properties</value>
</list>
</property>
</bean>
The Spring resolves ${APP_PROP_HOME} to the value stored in corresponding env property and your application is configured at runtime.

If I am reading your question correctly, You want to use an external properties file(i.e File is not in the application runtime class path). If that is the case you need to use the file tag
<value>file:///c:\application.properties</value>

You can use #Value to inject values from the env. Example:
private someFoo;
#Value("${systemProperties.someFoo}")
public void setSomeParam(String someParam) {
this.someFoo = someParam;
}

Related

Externalise properties working but not overriding existing default value in internal properties

I have a multi module maven project, each module is a jar with its own context file and a set of default properties bundled inside the JAR.
Basically in a core module I have this :
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:core.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
Now i have another module that build the application by loading all relevant XML and adding its own configuration file
<import resource="classpath*:core-context.xml" />
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:application.properties</value>
<value>#{systemEnvironment['foo_bar']}</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
What happen is that all my configurations files are working fine except the one that has default value. I can't override them in application.properties or in the file pointed by the system variable.
I tryed to force the configuration file using the spring.config.location. I do see in the tomcat the message saying that I have the configuration -Dspring.config.file=file:///... (windows path). But spring is totally ignoring it (no message in the logs from Spring).
I tried to switch to PropertySources class without more success.
I'd very like to not have to remove all default properties and put everything in an external file, because lot of parameters are internal to the application and don't have any value for a client.
So what I am missing here ?
Well I finnaly found after many hours so something very ... easy.
Here is the trick : Declare the property placeholder before the import.
When you declare files in a property placeholder, it's the last that win, it seems here it's the exact opposite : the first place holder win.
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:application.properties</value>
<value>#{systemEnvironment['foo_bar']}</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
<import resource="classpath*:core-context.xml" />

java.util.Properties: How do I retrieve values from Properties in spring config xml

I have a java.util.Properties object which has few key value pairs in it. I am trying to use this Property object in the spring configuration file i.e. define the key in the spring config and during runtime, it should get the value from the properties object.
For ex:
<bean id="test" class="com.sample.Test">
<constructor-arg value="${PROPERTY_KEY} />
</bean>
Now during the runtime, the constructor should get the value that is present in the Property object.
Is there a way to get this done ?
Note: I do not want to use config.properties here. Looking to use java.util.Properties
Thanks,
Rahul
<context:property-placeholder location="classpath:placeholder.properties"/>
or
<bean id="properties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:<file-name>.properties" />
</bean>
First you have to create bean to access your property file, like below
<bean id="appProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="singleton" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath*:localhost-mysql.properties</value>
<value>classpath*:mail-server.properties</value>
</list>
</property>
</bean>
<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="appProperties" />
</bean>
Next you can access Key-Value pair from your property files, like below
<bean id="mailServerSettings" class="com.ratormonitor.app.context.EmailSettings">
<property name="host" value="${mail.server.host}" />
<property name="port" value="${mail.server.port}" />
<property name="username" value="${mail.server.username}" />
<property name="password" value="${mail.server.password}" />
<property name="requestContextPath" value="${request.context.path}" />
</bean>
Hope this code will solve your problem.
You can use Spring Expression Language (SpEL) to get a java object value in spring configuration xml file.
An example :
<property name="misfireInstruction"
value="#{T(org.quartz.CronTrigger).MISFIRE_INSTRUCTION_FIRE_ONCE_NOW}"/>
So this is how I did:
As I said I had a java.util.Properties object. I then created a CustomProperty class which extended PropertySource>
public class CustomPropertySource extends PropertySource<Map<String, Object>>
Then in my main class I did the following:
AbstractApplicationContext context = new ClassPathXmlApplicationContext(new String[] {springConfigLocation, false);
context.getEnvironment().getPropertySources.addlast(new CustomPropertySource("custom", propertiesObject));
conext.refresh();
And then in the spring config file, I had to add this:
<context: property-placeholder ignore-unresolvable="true"/>
So in this way, I could fetch values for the keys defined in the spring config file, just like how we get the values from property files.
Thanks,
Rahul

Using Spring Integration SpEl inside value tag

I am trying to set the properties file inside a class that extends the PropertyPlaceholderConfigurer based upon the environment (local, dev, ref, qa, prod)
My folder structure looks like the following.
properties
environment.properties
server-local.properties
server-ref.properties
server-prod.properties
email-local.properties
email-ref.properties
email-prod.properties
cache-local.properties
cache-ref.properties
cache-prod.properties
The environment.properties has a property
environment.stage=local (or whatever env this is)
My Spring Integration context statements look something like this:
<context:property-placeholder location="classpath:properties/*.properties" />
<bean id="propertyPlaceholder" class="com.turner.bvi.BviPropertiesUtil">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations">
<list>
<value>classpath:properties/environment.properties</value>
<value>classpath:properties/*-${environment.stage}.properties</value>
</list>
</property>
</bean>
What I want to do is have only the properties file from the particular environment stage load (be it local, ref, prod .... etc.). How do I get just this second set of properties files to load based upon environment.stage?
Thanks for the help in advance.
You can use Spring profiles for this, it would be something like this:
<context:property-placeholder location="classpath:properties/*.properties" />
<beans profile="local">
<bean id="propertyPlaceholder" class="com.turner.bvi.BviPropertiesUtil">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations">
<list>
<value>classpath:properties/environment.properties</value>
<value>classpath:properties/*-local.properties</value>
</list>
</property>
</bean>
</beans>
<beans profile="dev">
<bean id="propertyPlaceholder" class="com.turner.bvi.BviPropertiesUtil">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations">
<list>
<value>classpath:properties/environment.properties</value>
<value>classpath:properties/*-local.properties</value>
</list>
</property>
</bean>
</beans>
...
Setting an environment variable can be done using spring_profiles_active (or spring_profiles_default). In Unix try exporting SPRING_PROFILES_DEFAULT=local
You can alternativerly use the JVM parameters like -Dspring.profiles.active=local
In case you need to maintain the environment variables as they are, you can implement a custom ActiveProfilesResolver as described in here: Spring Profiles: Simple example of ActiveProfilesResolver?
Thank you all for your help. I was able to take snippets of all your suggestions and come up with a solution using "environmentProperties" in the Spring context.
The problem with trying to use a in the context was that it had not been set at the time MY class was trying to resolve the ${environment.stage}... Or at least that is what I gathered from searching other posts.
If my properties file structure looks like this:
properties
environment.properties
server-local.properties
server-ref.properties
server-prod.properties
email-local.properties
email-ref.properties
email-prod.properties
cache-local.properties
cache-ref.properties
cache-prod.properties
I was able to set the Environment property 'env' to the correct value through Chef or through a Docker container and use the following code.
<!-- Register the properties file location -->
<context:property-placeholder location="classpath:properties/*.properties" />
<bean id="propertyPlaceholder" class="com.turner.bvi.BviPropertiesUtil">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations">
<list>
<value>classpath:properties/environment.properties</value>
<value>classpath:properties/*-#{systemEnvironment['env']}.properties</value>
</list>
</property>
</bean>
I could have put each of the property sets under its own directory and had
<value>classpath:properties/#{systemEnvironment['env']}/*.properties</value>
Again, thank you all for your help.

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 collect spring properties from multiple files for use on a single bean

I haven't gotten my head wrapped around Spring yet, so correct me if this question doesn't make sense...
I have a PropertyPlaceholderConfigurer
<bean id="rdbmPropertiesPlacholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" lazy-init="false">
<property name="location" value="classpath:/properties/rdbm.properties" />
</bean>
And I have a bean being injected I guess?
<bean id="PortalDb" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${hibernate.connection.driver_class}" />
<property name="url" value="${hibernate.connection.url}" />
<property name="username" value="${hibernate.connection.username}" />
<property name="password" value="${hibernate.connection.password}" />
...
What I want is a second placeholder pointing to a different properties file with the username/password so that I can split up the properties into two different files. Then the database connection information can be separate from the db username/password, and I can source control one and not the other.
I've tried basically copying the rdbmPropertiesPlaceholder with a different id and file and trying to access the properties, but it doesn't work.
This code is from the uPortal open source web portal project.
Using this notation lets you specify multiple files:
<bean id="rdbmPropertiesPlacholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" lazy-init="false">
<property name="locations">
<list>
<value>classpath:/properties/rdbm.properties</value>
<value>classpath:/properties/passwords.properties</value>
</list>
</property>
</bean>
The propertyplaceholderconfigurerer just merges all of these to look like there's only one, so your bean definitions do not know where the properties come from.
The org.springframework.beans.factory.config.PropertyPlaceholderConfigurer can do this (as already answered. What you may want to do is make use of the name spacing so that you can refer to same-named properties from both files without ambiquity. For your example, you can do this:
<bean id="generalPropertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:/properties/general.properties"/>
</bean>
<bean id="db.PropertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:/properties/rdbm.properties" />
<property name="placeholderPrefix" value="$db{" />
<property name="placeholderSuffix" value="}" />
</bean>
In your context files, you can now refer to general properties with ${someproperty}, and refer to rdbm properties with $db{someproperty}.
This will make your context files much cleaner and clearer to the developer.

Categories