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.
Related
Currently in my data context XML file, it's looking up values to substitute from a application.properties file:
<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" lazy-init="default">
<property name="location" value="classpath:application.properties" />
</bean>
<bean id="appleDataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="url" value="${apple.url}" />
</bean>
I'd like to change this from being looked up from the application.properties file, to being read out of a Properties/Map object.
This is because the configuration could come from more than one place (i.e. not just the classpath), and this logic is already implemented in my application.
I've seen MapPropertySource but I think while that can be done when the bean is configured in Java, I'm not sure this logic can be implemented when working with the XML file alone?
One of our team has implemented loading properties this way (see pseudo code below) and advises this approach is right as the client application using this is free to keep the properties in any file. Contrary to the widely used propertyplaceholderconfigurer.
application-context.xml
<bean class="com.mypackage.Myclass">
<property name="xml" value="classpath:"{com.myapp.myproperty1}"> </property>
</bean>
config.properties
com.myapp.myproperty1=data.xml
edit: I should have added it is data.properties and not data.xml. We want to load a property file (this property file is given in the config.properties as a "property".
com.myapp.myproperty1=data.properties
java class
import org.springframework.core.io.Resource;
public class Myclass {
private Resource xmlField;
// setter & getter methods..
}
Is it right to use spring core.io.Resource?
Another reason is the client application wants to load a environment specific configuration. I suggested use the propertyconfigurer and use maven profiles to generate the environment specific build
Can you please advise which one suits which case? and if it differs in different scenarios, please help me point out them?
thanks
You can put the properties in any file and still use PropertyPlaceholderConfigurer. Here's an example that satisfies both your coworker's concerns and your desire for environment specific stuff:
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- default settings -->
<value>classpath:MyCompany.properties</value>
<!-- environment-specific settings -->
<value>classpath:MyCompany.${mycompany.env:dev}.properties</value>
<!-- keep your coworker happy -->
<value>classpath:${mycoworker}</value>
<!-- allows emergency reconfiguration via the local file system -->
<value>file:///${user.home}/MyCompany.properties</value>
</list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="ignoreResourceNotFound" value="true" />
<!-- should be validated separately, in case users of the library load additional properties -->
<property name="ignoreUnresolvablePlaceholders" value="false"/>
</bean>
If you pass in no -D arguments, then you'll pick up the following properties files, where properties in the later files overwrite previously determined values.
MyCompany.properties off the classpath
MyCompany.dev.properties off the classpath
$HOME/MyCompany.properties if it exists
To swap in a production config for #2, just pass -Dmycompany.env=prod to java. Similarly your coworker can pass -Dmycoworker=/some/path/config.properties if he/she wants.
I'm not sure why a PropertyPlaceholderConfigurator wouldn't have been the correct choice.
I've almost always handled environment-specific configs via a customized PPC that can either (a) get a -D parameter on startup, and/or (b) use the machine name, to decide which property file to load.
For me, this is more convenient than bundling the information in via Maven, since I can more easily test arbitrary configurations from whatever machine I'm on (using a -D property).
+1 for Dave's suggestion. You should be using PropertyPlaceholderConfigurer for loading\reading properties. Here is the example i just pulled out from my previous project if you wonder how to use this. This example is for loading multiple properties files but the concept is same. Good luck.
<bean id="projectProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>classpath:config.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="projectProperties" />
</bean>
<bean id="uniqueAssetIdRetriever" class="com.mypackage.Myclass">
<property name="xml" value="${com.myapp.myproperty1}" />
</bean>
There is this tag <terracottaConfig url="host1:9510,host2:9510,host3:9510"/> in ehcache.xml file inside spring web application. I want to externalize url attribute of this tag. Value of URL should be replaced by a property from external file. It will be very helpful if you suggest any solution to this problem.
You can put something like this -
<terracottaConfig url="${terracotta.config.location}" /> , however the big catch is that this will be loaded only from the system properties. It is not resolved from PropertyPlaceHolder as it is not a Spring configuration file.
So if you want to use an external config file, you will basically have to programatically set this system property before the Spring Application starts loading up the ehcache.xml file - one way to do that will be write your custom ServletContextListener to load up your properties file and set the system property based on that, this way when the ehcache.xml is loaded up it would be able to resolve the place holder properly.
Your answer helped me to solve my problem. I just want to add that instead of setting system property through program, I am using util:properties as follows
<bean id="sysProperties" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" value="#{#systemProperties}"/>
<property name="targetMethod" value="putAll"/>
<property name="arguments">
<util:properties>
<prop key="propertyname_used_in_ecache_xml">#{proerties_defined_using_property_factory['propertyname_defined_in_external_properties_file']}</prop>
</util:properties>
</property>
</bean>
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" depends-on="sysProperties">
<property name="configLocation">
<value>classpath:ehcache.xml</value>
</property>
</bean>
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>
One of our team has implemented loading properties this way (see pseudo code below) and advises this approach is right as the client application using this is free to keep the properties in any file. Contrary to the widely used propertyplaceholderconfigurer.
application-context.xml
<bean class="com.mypackage.Myclass">
<property name="xml" value="classpath:"{com.myapp.myproperty1}"> </property>
</bean>
config.properties
com.myapp.myproperty1=data.xml
edit: I should have added it is data.properties and not data.xml. We want to load a property file (this property file is given in the config.properties as a "property".
com.myapp.myproperty1=data.properties
java class
import org.springframework.core.io.Resource;
public class Myclass {
private Resource xmlField;
// setter & getter methods..
}
Is it right to use spring core.io.Resource?
Another reason is the client application wants to load a environment specific configuration. I suggested use the propertyconfigurer and use maven profiles to generate the environment specific build
Can you please advise which one suits which case? and if it differs in different scenarios, please help me point out them?
thanks
You can put the properties in any file and still use PropertyPlaceholderConfigurer. Here's an example that satisfies both your coworker's concerns and your desire for environment specific stuff:
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- default settings -->
<value>classpath:MyCompany.properties</value>
<!-- environment-specific settings -->
<value>classpath:MyCompany.${mycompany.env:dev}.properties</value>
<!-- keep your coworker happy -->
<value>classpath:${mycoworker}</value>
<!-- allows emergency reconfiguration via the local file system -->
<value>file:///${user.home}/MyCompany.properties</value>
</list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="ignoreResourceNotFound" value="true" />
<!-- should be validated separately, in case users of the library load additional properties -->
<property name="ignoreUnresolvablePlaceholders" value="false"/>
</bean>
If you pass in no -D arguments, then you'll pick up the following properties files, where properties in the later files overwrite previously determined values.
MyCompany.properties off the classpath
MyCompany.dev.properties off the classpath
$HOME/MyCompany.properties if it exists
To swap in a production config for #2, just pass -Dmycompany.env=prod to java. Similarly your coworker can pass -Dmycoworker=/some/path/config.properties if he/she wants.
I'm not sure why a PropertyPlaceholderConfigurator wouldn't have been the correct choice.
I've almost always handled environment-specific configs via a customized PPC that can either (a) get a -D parameter on startup, and/or (b) use the machine name, to decide which property file to load.
For me, this is more convenient than bundling the information in via Maven, since I can more easily test arbitrary configurations from whatever machine I'm on (using a -D property).
+1 for Dave's suggestion. You should be using PropertyPlaceholderConfigurer for loading\reading properties. Here is the example i just pulled out from my previous project if you wonder how to use this. This example is for loading multiple properties files but the concept is same. Good luck.
<bean id="projectProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>classpath:config.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="projectProperties" />
</bean>
<bean id="uniqueAssetIdRetriever" class="com.mypackage.Myclass">
<property name="xml" value="${com.myapp.myproperty1}" />
</bean>