property-placeholder location from another property - java

I need to load some properties into a Spring context from a location that I don't know until the program runs.
So I thought that if I had a PropertyPlaceholderConfigurer with no locations it would read in my.location from the system properties and then I could use that location in a context:property-placeholder
Like this
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<context:property-placeholder location="${my.location}"/>
but this doesn't work and nor does location="classpath:${my.location}"
Paul

You can do this with a slightly different approach. Here is how we configure it. I load default properties and then overrided them with properties from a configurable location. This works very well for me.
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations">
<list>
<value>classpath:site/properties/default/placeholder.properties
</value>
<value>classpath:site/properties/${env.name}/placeholder.properties
</value>
</list>
</property>
</bean>

The problem here is that you're trying to configure a property place holder using property placeholder syntax :) It's a bit of a chicken-and-egg situation - spring can't resolve your ${my.location} placeholder until it's configured the property-placeholder.
This isn't satisfactory, but you could bodge it by using more explicit syntax:
<bean class="org.springframework.beans.factory.config.PropertyPlaceHolderConfigurer">
<property name="location">
<bean class="java.lang.System" factory-method="getenv">
<constructor-arg value="my.location"/>
</bean>
</property>
</bean>

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

Can't load multiple property files in spring's application context

I have three property files placed in a resource folder in classpath. The problem i am facing is while i am able to load invidual files separately i am unable to load them when they are declared together.
Please see the XML below:
<bean name="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="resources\label"/>
</bean>
This is working but the XML given below isn't:
<bean name="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames" value="resources\label,resources\button,resources\messages"/>
<property name="cacheSeconds" value="1"/>
</bean>
I wish to declared them together as I wish to use a single bean to access all three files. Help required!
Found the answer . It should be like this `
<property name="basenames">
<list>
<value>classpath:resources\label</value>
<value>classpath:resources\button</value>
<value>classpath:resources\messages</value>
</list>
</property>
</bean>
Do it like this
<property name="basenames">
<list>
<value>resources\label</value>
<value>resources\button</value>
<value>resources\messages</value>
</list>
</property>

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 call properties file from another properties file using Spring PropertyPlaceholderConfigurer?

I'm working on a command-line Java application using Spring. I have multiple properties files stored in different locations and one properties file containing the path for all those properties. I'm using PropertyPlaceholderConfigurer, to read properties containing the locations of different properties files. I'm not sure of the best way of handling multiple properties.
The application works like this: I will pass the path for first properties file using JVM command -Dmypath=parent.properties. The properties file will look like this:
child1=/location1/child1.properties
child2=/location2/child2.properties
so on
My Parent properties configuration looks like this:
<bean id="parentProperty" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>${mypath}</value>
</list>
</property>
</bean>
The child1 configuration looks:
<bean id="child1Property" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>${child1}</value>
</list>
</property>
</bean>
Now when I call child1 it fails to load the properties.
I have load the parent property file first and then set the particular child1 and child2 variable in system environment variable and load from system environment variable. and its working fine.
code :
<context:property-placeholder location="file:${mypath}/*.properties" ignore-unresolvable="true" />
<bean id="systemPrereqs" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" value="#{#systemProperties}" />
<property name="targetMethod" value="putAll" />
<property name="arguments">
<!-- The new Properties -->
<util:properties>
<prop key="LOG_LOCATION">${log.location}</prop>
<prop key="child1">${child1}</prop>
</util:properties>
</property>
</bean>
<context:property-placeholder location="file:#{systemProperties['child1']}/*.sql" ignore-unresolvable="true" />
The execution order of BeanFactoryPostProcessors such as PropertyPlaceholderConfigurer can be set by setting the property "order"(see Ordered). By setting the execution priority of parentProperty higher that that of child1Property you can ensure that parentProperty runs first, configuring the value of ${child1}.
It may be easier to load the properties from the classpath, where the locations are included in your classpath, instead of in a file, and then the following will load all your property files.
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations">
<list>
<value>classpath*:*.properties</value>
</list>
</property>
</bean>

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