Can one Spring PropertyPlaceholderConfigurer configure another one? - java

I have a PropertyPlaceholderConfigurer like this:
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:assuredlabor/margarita-${runningMode}.properties</value>
</list>
</property>
</bean>
I would like to be able to specify my running mode in web.xml like this:
<context-param>
<param-name>runningMode</param-name>
<param-value>production</param-value>
</context-param>
So I put this bean ABOVE the 'main' property bean I described above:
<bean id="servletPropertyPlaceholderConfigurer" class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
</bean>
But that doesn't seem to work.
Is this possible with Spring? I am using version 2.5 right now.
I found this similar question:
PropertyPlaceholderConfigurer with Tomcat & ContextLoaderListener
But there is no discussion of the ServletContextPropertyPlaceholderConfigurer, so I think it is a legitimate question.

You can't do this in spring 2, without some custom coding I don't think, since one property placeholder cannot configure another.
You need to use spring 3 to get this out of the box. To accomplish this, you have to create a bean that somehow has the value you want, and use spring-el to reference that spring when setting up your property placeholder. There's a special bean for getting individual servlet context parameters as show below:
<bean id="runningMode" class="org.springframework.web.context.support.ServletContextAttributeFactoryBean">
<property name="attributeName" value="runningMode" />
</bean>
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:assuredlabor/margarita-#{runningMode}.properties</value>
</list>
</property>
</bean>
And then you can just refer to any of the properties in the normal ${} syntax

From the source code:
Subclass of PropertyPlaceholderConfigurer that resolves placeholders as ServletContext init parameters (that is, web.xml context-param entries).
Can be combined with "locations" and/or "properties" values in addition to web.xml context-params. Alternatively, can be defined without local properties, to resolve all placeholders as web.xml context-params (or JVM system properties).
If a placeholder could not be resolved against the provided local properties within the application, this configurer will fall back to ServletContext parameters. Can also be configured to let ServletContext init parameters override local properties (contextOverride=true).
Optionally supports searching for ServletContext attributes: If turned on, an otherwise unresolvable placeholder will matched against the corresponding ServletContext attribute, using its stringified value if found. This can be used to feed dynamic values into Spring's placeholder resolution.
If not running within a WebApplicationContext (or any other context that is able to satisfy the ServletContextAware callback), this class will behave like the default PropertyPlaceholderConfigurer. This allows for keeping ServletContextPropertyPlaceholderConfigurer definitions in test suites.
As I understand it, that implies that you can use just a single configurer:
<bean id="propertyPlaceholderConfigurer" class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:assuredlabor/margarita-${runningMode}.properties</value>
</list>
</property>
</bean>

Related

Spring Context: It's possible to define variables (not properties) in a XML and use it in runtime to obtain specific referenced beans?

I don't have much experience working with Spring Context and I don't know if this is possible...I'm trying to set into a Spring XML file a variable to define a bean reference (not a property).
Now I have a specidific xml:
keyIntegrator-key1.xml
<import resource="classpath:/events/key-events.xml" />
<context:annotation-config />
<bean id="keyIntegrator" class="com.emulated.KeySimulator" >
<property name="readList">
<list>
<bean class="com.emulated.ListEventGenerator">
<property name="eventList">
<ref bean="key-1-ok"/>
</property>
</bean>
</list>
</property>
</bean>
All the keys were defined in another xml file (key-events.xml).
I have to load in Java runtime the bean "keyIntegrator" with only one key, that is a input parameter in the Java program (I use the param to decide the xml file to load)
My question is if it's possible to define a variable inside the xml file and get the referenced bean using this variable:
Something like this:
keyIntegrator-generic.xml
<import resource="classpath:/events/key-events.xml" />
<context:annotation-config />
<bean id="keyIntegrator" class="com.emulated.KeySimulator" >
<property name="readList">
<list>
<bean class="com.emulated.ListEventGenerator">
<property name="eventList">
<ref bean="key-{inputKeyParam}-ok"/>
</property>
</bean>
</list>
</property>
</bean>
In the Java program I will need to pass the param to get the bean, something like this:
keySimulatorBean = (KeySimulator) context.getBean("keyIntegrator", "1");
There any way possible to make this ?
Thank you very much!
Thank you very much, it worked for me :)
I setted the system property value in the Java code:
System.setProperty("inputKeyParam", "1");
It is possible to do using Spring Expression Language.
For example reference to the bean can be defined using system property
<ref bean="key-#{systemProperties.inputKeyParam}-ok"/>
It will allow to reference different beans depending on provided VM option value, e.g. -DinputKeyParam=1

Spring - how to use PropertyPlaceholderConfigurer to dynamically load files

i have the next properties files with Spring Framework
config.properties
with content
environment=devel //posible values: devel, testing, prod
and with the previous environment property, choose some of the following files to load dynamically
config-service1-devel.properties
config-service1-testing.properties
config-service1-prod.properties
config-serviceN-devel.properties
config-serviceN-testing.properties
config-serviceN-prod.properties
and then, with spring i want load the properties, i'm solve to load the first properties file but i dont understand how to use expression language to complete the values of the dependent properties.
<bean id="MainApplicationProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location"
value="file://#{systemProperties['jboss.server.home.dir']}/conf/services.properties" />
<property name="placeholderPrefix" value="$mainProperty{" />
<property name="placeholderSuffix" value="}" />
</bean>
<bean id="SecondApplicationProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
depends-on="MainApplicationProperties">
<property name="locations">
<list>
<value>file://#{systemProperties['jboss.server.home.dir']}/conf/serviceOne/service1-$mainProperty{environment}.properties</value>
<value>file://#{systemProperties['jboss.server.home.dir']}/conf/serviceTwo/service2-$mainProperty{environment}.properties</value>
<value>file://#{systemProperties['jboss.server.home.dir']}/conf/serviceN/serviceN-$mainProperty{environment}.properties</value>
</list>
</property>
</bean>
the error output is the next,
java.io.FileNotFoundException: /..../conf/serviceOne/service1-$mainProperty{environment}.properties (No such file or directory)
my opinion is, the value has not replaced
helpme, thanks
The problem is that when BeanFactoryPostProcessors are starting to be invoked, they are already instantiated. So even thou the first PropertyPlaceholderConfigurer modifies the bean definition of the second PropertyPlaceholderConfigurer, it has no effect as both beans have been already instantiated.

Is it possible to specify a context property placeholder at runtime

I have a standalone jar that uses spring. The config in my spring xml uses placeholders of which I've been replacing when compiling with maven. Example spring config:
<bean id="foo" class="package.Foo">
<property name="host" value="${db.host}" />
</bean>
Instead of replacing ${db.host} using maven I'd like to pass in a properties file at runtime, e.g.
java -jar Application.jar productionDB.properties
This would allow me to switch the db host at runtime by passing in the production db properties file or the testing db properties file.
Is it possible to do this or are there any better ways of achieving the same goal?
You could specify your property file as a System Property, e.g.:
java -jar Application.jar -DappConfig=/path/to/productionDB.properties
Then you should be able to reference that in your application context:
<context:property-placeholder location="file:${appConfig}"/>
<bean id="foo" class="package.Foo">
<property name="host" value="${db.host}" />
</bean>
You could use a PropertyPlaceholderConfigurer to use a .properties file to pass in the required variables.
<bean id="placeholderConfig"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:productionDB.properties</value>
</list>
</property>
</bean>
You can leave your bean declaration as is. The properties will be automatically taken from the productionDB.properties file.
There are a few options:
Set your resources via your container's JNDI and use Spring's <jee:jndi-lookup/>.
Use Spring's <context:property-placeholder location="classpath:/myProps.properties" />. I prefer this short-hand over the "full" bean definition because Spring will automatically use the correct implementation (PropertyPlaceholderConfigurer for Spring < 3.1, or PropertySourcesPlaceholderConfigurer for Spring 3.1+). Using this configuration, you would just drop the myProps.properties at the root of your classpath (${TOMCAT_HOME}/lib for example).
You can pass the values using the context:property-placeholder. So your setup would be something like:
<context:property-placeholder location="file://opt/db.properties"/>
Then when you are wiring up your Foo service, you can use the property names in your config, such as
<bean id="foo" class="package.Foo">
<property name="host" value="${db.host}" />
</bean>
Then just use the different set of files for each environmnet
See the spring docs for more details.

How do I use a variable property file name in Spring?

I'm trying to separate an application's configuration files from its war.
I want to keep all properties files in a directory on the disk. Then, the only property required inside the war would be the path to the configuration directory (let's say that would be in a file named config.properties):
config.dir = /home/me/config
Now in the spring configuration, I want to load this file (so that I know where the others are), and then the external files:
<bean id="propertySourcesPlaceholderConfigurer"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:META-INF/config.properties</value>
<value>${config.dir}/other.properties</value>
</list>
</property>
</bean>
But this doesn't work, the placeholder is not resolved:
java.io.FileNotFoundException: class path resource [${config.dir}/config.properties] cannot be opened because it does not exist
I also tried using a separate bean of type PropertySourcesPlaceholderConfigurer - it didn't help much.
Do you know how I could accomplish this?
The problem is that the configurer bean has to be fully constructed before it can resolve placeholders in the other bean definitions in the context, so you can't use a placeholder expression in the definition of the configurer that would need to be resolved by the configurer itself.
You could instead put the path to your config dir into web.xml as a context-param
<context-param>
<param-name>configDir</param-name>
<param-value>/home/me/config</param-value>
</context-param>
and then access it as #{contextParameters.configDir} in your Spring config
<bean id="propertySourcesPlaceholderConfigurer"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>#{contextParameters.configDir}/other.properties</value>
</list>
</property>
</bean>
Or you may be able to do it with two separate configurer beans with different values of placeholderPrefix, one loading the config.properties and then filling in the #{config.dir} placeholder in the other, which then loads the external config file.
This can be resolved by registering a PropertySource for the default environment. One of the ways this can be done is using Java Configuration:
#Configuration
#PropertySource("classpath:META-INF/config.properties")
public class MyConfig {
}
With this in place the placeholder should get resolved:
<bean id="propertySourcesPlaceholderConfigurer"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>${config.dir}/other.properties</value>
</list>
</property>
</bean>

PropertyPlaceholderConfigurer + PropertiesFactoryBean only resolving location properties

I am having trouble getting PropertyPlaceholderConfigurer to work in my current configuration. Given the following block of code in my applicationContext.xml:
<bean id="myProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<!-- Order matters, last one to create a property wins! -->
<value>classpath:default.properties</value>
<value>file:${MYAPP_PROPERTIES_LOCATION:badurl}/application.properties</value>
<value>file:${user.home}/developer.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true"/>
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertiesPlaceholderConfigurer">
<property name="properties" ref="myProperties"/>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="searchSystemEnvironment" value="true"/>
</bean>
Then in the default.properties files I have the following (these are test properties):
property1=prop1val
property2=${property1}
What works: the propertyConfigurer correctly resolves both the environment variable MYAPP_PROPERTIES_LOCATION and the System variable user.home. However, then the final properties object is created by the factory bean, the resulting properties are [property1=prop1val, property2=${property1}].
With this configuration, how can I get the properties inside the myProperties bean to resolve their placeholders?? I have done plenty of research to include tracing through the spring code - and I can see how and why this isn't being done. I'm hoping there is some setting I'm just missing! This is my first post so be easy on me :)
You have proper configuration of spring and the property files. That why you are being able to read the data from the file. Everything is proper with what spring is doing too, let me explain a bit more....
As you told below is your property file,
property1=prop1val
property2=${property1}
Remember, it is a pretty text file with key value pairs and it can not take variables. Here, if you are intended to copy the value of property1 to property2 dynamically it is not going to happen. That is not the way we are supposed to use a property file.
Property file is supposed to be simple text file with key-value pairs. So, keep the keys atomic so that you can construct the required data logically from the application or inside your applicationcontext.xml file.
I am not sure why you are using PropertiesFactoryBean. Can you please try just the following (Not sure if you already tried it and any issues you faced)
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- Order matters, last one to create a property wins! -->
<value>classpath:default.properties</value>
<value>file:${MYAPP_PROPERTIES_LOCATION:badurl}/application.properties</value>
<value>file:${user.home}/developer.properties</value>
</list>
</property>
</bean>

Categories

Resources