Use environment variable in Spring beans.xml - spring

I have a peculiar requirement of setting the value of some bean from system environment variable , is this thing possible in spring ?
Pseudo code
<bean id="connectionFactory"
class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg value="testhost" />
<property name="username" value="$SystemEnv.RabbitUserName" />
<property name="password" value="$SystemEnv.RabbitPassword" />
</bean>
Now these RabbitUserName and RabbitPassword are externalized in some environmental variable which is outside .war file .
Is there any elegant way to achieve the same in spring ?

To get the System Environment Variable you can use this for the value:
#{ systemEnvironment['RabbitUserName'] }
The #{ systemProperties['RabbitUserName'] } mentioned in another answer won't give you a System Environment Variable, rather it will read a Java system property ie, those things you can set on the command line when starting the JVM, eg:
java -DRabbitUserName="reisen" etc...
However, this is probably a better choice of place for a connection credential than an environment variable, as, for example, you can then configure it differently for different launch configurations and it is also slightly less exposed than an environment value - which all the other applications also have full visibility on.
For more information on the difference between environent variables and system properties see Java system properties and environment variables
These are examples of the Spring Expression Language (SpEL), and you can also use it with annotation metadata, for example
#Value("#{ systemEnvironment['RabbitUserName'] }")
For more information on SpEL you can take a look at the Spring reference here: https://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/expressions.html

This should work.
<bean id="connectionFactory"
class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg value="testhost" />
<property name="username" value="#{systemProperties['RabbitUserName']}" />
<property name="password" value="#{systemProperties['RabbitPassword']}" />
</bean>

Related

Apache Camel Spring DSL, referring to an environment variable HOSTNAME

I am using Apache Camel 2.16.0 with Spring DSL
I have a Spring context XML in which I have defined a Property PlaceHolder to read the properties from various files as follows-
<bean id="propertyPlaceholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="ignoreResourceNotFound" value="false"/>
<property name="locations">
<list>
<value>classpath:/properties/versioning.properties</value>
<value>classpath:/properties/#{inetAddress.hostName}.properties</value>
</list>
</property>
</bean>
<bean id="inetAddress" class="java.net.InetAddress" factory-method="getLocalHost"/>
The property values are used to construct other beans such as -
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.gjt.mm.mysql.Driver"/>
<property name="url" value="${${LIVE_}DATASOURCE_URL}"/>
<property name="username" value="${${LIVE_}DATASOURCE_USERNAME}"/>
<property name="password" value="${${LIVE_}DATASOURCE_PASSWORD}"/>
</bean>
This works fine, I can see the beans being created.
I also have another Spring Context XML in the same app which has a camel context and I want to use the some other properties defined in the same properties files. I know that camel supports Spring Property Placeholder, see below excerpts from the context -
<camelContext id="charge-process-context" xmlns="http://camel.apache.org/schema/spring">
<propertyPlaceholder id="properties"
location="classpath:/properties/versioning.properties,
properties/${env:HOSTNAME}.properties"
xmlns="http://camel.apache.org/schema/spring" />
.....
.....
</camelContext>
As part of this context, I have a route that uses https component that uses the values from the property file such as below -
<to uri="https4:{{LIVE_AUTH_RESPONSE_HOST}}:{{LIVE_AUTH_RESPONSE_PORT}}/{{LIVE_AUTH_RESPONSE_CONTEXT_PATH}}"/>
This route does not start and throws following exception -
Caused by: java.lang.IllegalArgumentException: Cannot find system environment with key: HOSTNAME
at org.apache.camel.util.FilePathResolver.resolvePath(FilePathResolver.java:54)
at org.apache.camel.component.properties.PropertiesComponent.parseLocations(PropertiesComponent.java:434)
at org.apache.camel.component.properties.PropertiesComponent.parseUri(PropertiesComponent.java:163)
at org.apache.camel.component.properties.PropertiesComponent.parseUri(PropertiesComponent.java:148)
at org.apache.camel.impl.DefaultCamelContext.resolvePropertyPlaceholders(DefaultCamelContext.java:2261)
at org.apache.camel.model.ProcessorDefinitionHelper.resolvePropertyPlaceholders(ProcessorDefinitionHelper.java:730)
at org.apache.camel.model.ProcessorDefinition.createOutputsProcessorImpl(ProcessorDefinition.java:427)
at org.apache.camel.model.ProcessorDefinition.createOutputsProcessor(ProcessorDefinition.java:413)
at org.apache.camel.model.ProcessorDefinition.createOutputsProcessor(ProcessorDefinition.java:165)
at org.apache.camel.model.ExpressionNode.createFilterProcessor(ExpressionNode.java:109)
at org.apache.camel.model.WhenDefinition.createProcessor(WhenDefinition.java:74)
at org.apache.camel.model.WhenDefinition.createProcessor(WhenDefinition.java:32)
at org.apache.camel.model.ProcessorDefinition.createProcessor(ProcessorDefinition.java:483)
at org.apache.camel.model.ChoiceDefinition.createProcessor(ChoiceDefinition.java:135)
at org.apache.camel.model.ProcessorDefinition.makeProcessorImpl(ProcessorDefinition.java:534)
at org.apache.camel.model.ProcessorDefinition.makeProcessor(ProcessorDefinition.java:495)
at org.apache.camel.model.ProcessorDefinition.addRoutes(ProcessorDefinition.java:219)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1069)
Please Note: I am deploying my application as a war file on Tomcat 8 on an AWS instance.
I have a Dev Environment on Windows 10 and I have found this working on the Windows OS. I have also seen that the file FilePathResolver.java in Apache Camel 2.16 uses System.getenv(key) to obtain the value i.e. System.getenv("HOSTNAME") which returns a null on AWS instance and a correct value on Windows 10. I also tried using env:hostname (small case letters for unix) but still no luck ...
I found a solution at http://camel.apache.org/using-propertyplaceholder.html
at Bridging Spring and Camel Property Placeholders
It mentions following -
The Spring Framework does not allow 3rd party frameworks such as Apache Camel to seamless hook into the Spring property placeholder mechanism. However you can easily bridge Spring and Camel by declaring a Spring bean with the type org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer, which is a Spring org.springframework.beans.factory.config.PropertyPlaceholderConfigurer type.

Jasypt and Weblogic environment variables

I am using jasypt 1.9.2 to encrypt a password in a property file for my spring REST service. I've added an environment variable to Weblogic via the Server startup arguments text box called APP_ENCRYTPION_PASSWORD, but that environment variable is not getting read by the jasypt. Here is the error:
ERROR o.s.web.servlet.DispatcherServlet - Context initialization failed
java.lang.NullPointerException: null
at org .jasypt.encryption.pbe.config.SimplePBEConfig.getPasswordCharArray(SimplePBEConfig.java:434) ~[jasypt-1.9.2.jar:na]
Here is the Weblogic environment variable logged during when the server starts up:
JAVA_OPTIONS= -Dother.vars=xxx -DAPP_ENCRYPTION_PASSWORD=password -Dmore.vars=yyy
I've traced the jasypt code and it seems jasypt does not parse the environment variables within JAVA_OPTIONS. I know this works for other frameworks like spring since we have other environment variables within JAVA_OPTIONS that spring has no issue reading.
I could add the environment variable to the startup scripts (setEnv.sh I think) for weblogic, but that will add the variable for all managed nodes instead of the one cluster my app is deployed to.
Is there a different way to configure jasypt within spring to get the environment variables within JAVA_OPTIONS?
Here is my spring config:
<bean
class="org.jasypt.spring31.properties.EncryptablePropertyPlaceholderConfigurer">
<constructor-arg>
<bean class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="config">
<bean class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndTripleDES" />
<property name="passwordEnvName" value="APP_ENCRYPTION_PASSWORD" />
</bean>
</property>
</bean>
</constructor-arg>
<property name="location">
<value>application.properties
</value>
</property>
</bean>
My application.properties file contents:
username=someuser
password=ENC(encryptedstring)
UPDATE for clarity:
Adding the environment variable to weblogic via setEnv.sh or Eclipse works just fine. It's only when I use the weblogic console to add the environment variable for a cluster that jasypt fails to parse the value since it is within JAVA_OPTIONS.
You can use passwordSysPropertyName instead of passwordEnvName. This way you can use -DpasswordSysPropertyName="mysecret"
<bean id="environmentVariablesConfiguration"
class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndDES" />
<property name="passwordSysPropertyName" value="APP_ENCRYPTION_PASSWORD" />
</bean>

Activating Spring Profile active JVM arguments in GWT hosted mode

I have a spring profile configuration as shown below
<beans profile="dev">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${db.driverClassName}" />
<property name="jdbcUrl" value="dfgdfg" />
<property name="user" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
</beans>
<beans profile="prod">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/Test"/>
</beans>
I am trying to make one of this active via the VM argument -Dspring.profiles.active="dev" . This works in Tomcat and so does the context-param route in Hosted mode via the gwt-maven-plugin but I can't get the VM arguments to work . I tried mvn -Dspring.profiles.active="dev" gwt:run also tried to pass -Dspring.profiles.active="dev" via the VM arguments under the JRE tab in run configurations along with the goal gwt:run . I also tried the environment tab and even -Dspring.profiles.active=dev but the NoSuchBeanDefinitionException doesn't budge . Is this because of the limited capability of the embedded server ?
No, simply gwt:maven plugin is kind of strange and it doesn't pass system properties to the launched JVM instance, and the only way to pass parameters is to put it into <extraJvmArgs> in plugin configuration e.g. in your case you have to add following to the configuration tag of gwt plugin:
<extraJvmArgs>-Dspring.profiles.active=dev</extraJvmArgs>
God knows why this works only this way, I wish there were some other normal way.

Can you configure velocity to use a toolbox file from the classpath?

I'm using spring with a VelocityViewResolver configured by Spring
<bean id="velocityViewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="order" value="1" />
<property name="suffix" value=".vm" />
<property name="contentType" value="text/html;charset=UTF-8" />
<property name="exposeSpringMacroHelpers" value="true" />
<property name="toolboxConfigLocation" value="/WEB-INF/velocityToolbox.xml" />
</bean>
This works if the velocityToolbox.xml file is under WEB-INF ; however I'm trying to put it in the classpath. Is there a way to do it ?
I've tried specifying toolboxConfigLocation as "classpath:/wherever/velocityToolbox.xml", but it does not find the resource, and I end up with no toolbox configured, and a NPE at runtime (for some reason, it seems like the code expect the location to start with a '/', or add the '/' itself before looking for the resource).
Given that the resource is located using ServletContext.getResourceAsStream, with the content of the toolboxConfigLocation property prefixed by a "/", is there a way I can "define" a resource in my spring config that would somehow 'point' to an actuall classpath resource ?
Any idea is welcome.
I believe it has to be a full path, eg:
/WEB-INF/classes/com/myapp/resources/toolbox.xml
The ServletToolboxManager#getInstance(ServletContext context, String toolboxConfigLocation) method is used to generate a ToolboxManager configured with the toolbox in order to create the ChainedContext used in the VelocityToolboxView.
This method pre-appends the path with a '/' if one does not exist, and then uses ServletContext#getResourceAsStream(String path) to read it in.
With this in mind, you'll have success if you set it as a full path from the context root.

Spring application context external properties?

i have a Spring application and its working well so far. Now i want the properties file in an external config folder and not in the packed jar to change things without the need to repack. This is what i got:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- <property name="locations" value="classpath:/springcontext.properties"/> -->
<property name="locations" value ="config/springcontext.properties" />
The outcommented one is working and the other one i dont get to work :/ Can someone help?
Edit:
Thx 4 comments so far.
Maybe my question wasnt clear enough :). I perform a Maven build and everything will be packaged and i want this folder to be NOT in the package nut next to the outcomming jar and in this folder i want the properties file. possible?
You can try something like this:
<context:property-placeholder
location="${ext.properties.dir:classpath:}/servlet.properties" />
And define ext.properties.dir property in your application server / jvm, otherwise the default properties location "classpath:/" (i.e., classes dir of .jar or .war) would be used:
-Dext.properties.dir=file:/usr/local/etc/
BTW, very useful blog post.
You can use file prefix to load the external application context file some thing like this
<context:property-placeholder location="file:///C:/Applications/external/external.properties"/>
<context:property-placeholder location="classpath*:spring/*.properties" />
If you place it somewhere in the classpath in a directory named spring (change names/dirs accordingly), you can access with above
<property name="locations" value ="config/springcontext.properties" />
this will be pointing to web-inf/classes/config/springcontext.properties
This blog can help you. The trick is to use SpEL (spring expression language) to read the system properties like user.home, to read user home directory using SpEL you could use #{ systemProperties['user.home']} expression inside your bean elements. For example to access your properties file stored in your home directory you could use the following in your PropertyPlaceholderConfigurer, it worked for me.
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>file:#{ systemProperties['user.home']}/ur_folder/settings.properties</value>
</property>
</bean>
This question is kind of old, but wanted to share something which worked for me. Hope it will be useful for people who are searching for some information accessing properties in an external location.
This is what has worked for me.
Property file contents:
PROVIDER_URL=t3://localhost:8003,localhost:8004
applicationContext.xml file contents: (Spring 3.2.3)
Note: ${user.home} is a system property from OS.
<context:property-placeholder system-properties-mode="OVERRIDE" location="file:${user.home}/myapp/latest/bin/my-env.properties"/>
<bean id="appsclusterJndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
<prop key="java.naming.provider.url">${PROVIDER_URL}</prop>
</props>
</property>
</bean>
${PROVIDER_URL} got replaced with the value in the properties the file
One way to do it is to add your external config folder to the classpath of the java process. That's how I've often done it in the past.
<context:property-placeholder location="file:/apps/tomcat/ath/ath_conf/pcr.application.properties" />
This works for me.
Local development machine path is C:\apps\tomcat\ath\ath_conf and in server /apps/tomcat/ath/ath_conf
Both works for me

Resources