123

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

piet.t
  • 11,718
  • 21
  • 43
  • 52
jai
  • 21,519
  • 31
  • 89
  • 120
  • Do you want to read a Java system property, or an environment variable (in Linux, for example)? This is quite important. Thank you for this clarification. I know that the question is old, but... – Valerio Bozz Feb 21 '23 at 15:15

13 Answers13

114

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']}

eis
  • 51,991
  • 13
  • 150
  • 199
amra
  • 16,125
  • 7
  • 50
  • 47
  • what's the java ... -Denv=QA means ? – fresh_dev Jan 18 '12 at 14:47
  • 3
    You set a java system property value. You can read this value in code like `assert System.getProperty("env") == "QA";` – amra Jan 20 '12 at 17:14
  • I think this answer is incorrect, this doesn't allow reading system environment variables (i.e. OS-level variables set with `export`, etc), it only allows reading Java system properties. – amoe Nov 15 '13 at 13:24
  • 3
    -Dprop=... sets a java property in command line. You can read this property via `System.getProperty("prop")`. If you would like to read a OS property then use `System.getenv("os-env-variable")`. See javadoc: http://docs.oracle.com/javase/6/docs/api/java/lang/System.html – amra Jan 20 '14 at 16:54
  • 27
    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']}`. – Yiling Jul 24 '14 at 07:02
  • This works fine, I just tried it. #{systemEnvironment['ENV_VARIABLE_NAME']} – Lyndon Nov 18 '16 at 09:25
  • This worked for me, My propery file (from among 5 prop files for each server )was present in folder resources>config folder – veritas Mar 24 '23 at 08:27
62

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

Popeye
  • 1,548
  • 3
  • 25
  • 39
Dariusz
  • 21,561
  • 9
  • 74
  • 114
  • 1
    Are env variables also accessible through the `Environment` interface ? – Nikhil Sahu Dec 11 '16 at 18:06
  • @NikhilSahu Yes, they are. You access them with the same key as you would when querying `java.lang.System` eg to get OS type you'd do `env.getProperty("os.name")` assuming `env` is your instance of `org.springframework.core.env.Environment`. – Ninetou Jun 27 '17 at 18:15
  • 1
    `@Autowired private Environment environment;` doesn't work for my `Component` the environment is always null –  Jul 05 '18 at 11:11
51

Check this article. It gives you several ways to do this, via the PropertyPlaceholderConfigurer which supports external properties (via the systemPropertiesMode property).

Emil Sierżęga
  • 1,785
  • 2
  • 31
  • 38
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
27

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.

Istao
  • 7,425
  • 6
  • 32
  • 39
9

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>
Brad Parks
  • 66,836
  • 64
  • 257
  • 336
8

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>
Community
  • 1
  • 1
Mikus
  • 322
  • 3
  • 7
6

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>
eis
  • 51,991
  • 13
  • 150
  • 199
4

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}
António Ribeiro
  • 4,129
  • 5
  • 32
  • 49
Justin Patel
  • 1,323
  • 10
  • 5
1

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>
Atit Shah
  • 11
  • 2
1

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 .

Sunil
  • 364
  • 1
  • 14
1

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.

StevenWernerCS
  • 839
  • 9
  • 15
Jaikrat
  • 1,124
  • 3
  • 24
  • 45
1

Updated version (2020).

Use System.getenv("ENV_VARIABLE")

Rubén Morales
  • 332
  • 4
  • 6
0

Amra solution helped me to get this right. In my case, each server picked up its own property file who name came from the system variable.

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath:/config/#{systemProperties['APPLICATION_PROPERTIES_FILE']}"/>
    </bean>  

After this, I had to add the property key via Spring JSTL expression e.g

class="org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter">
        <property name="signingKey" value="${oauth.signingkey}"/>
        <property name="jwtClaimsSetVerifier" ref="OauthClaimVerifier"/>
    </bean>
veritas
  • 378
  • 1
  • 6
  • 16