As I can see there were plenty questions regarding properties in Spring already asked, but what I want to achieve is a little unusal.
Let assume that I have cp.prop.file.properties on the classpath
external.prop.file.path=file:./path/to/external.prop.file.properties
Now if in my spring context I will declare something like this
<context:property-placeholder location="classpath:cp.prop.file.properties" />
<context:property-placeholder location="${external.prop.file.path}" />
I suppose it will not work properly. I do not have possibility to check at the moment.
I believe my intentions on what I am trying to achive are fairly clear. In general, I want to have some properties that are build dependet and are not configurable and some that configurable and are externalized. And the path to the latter is defined during build.
You can load more than one property file with following syntax
<context:property-placeholder
location="classpath:a.properties, file:/path/to/myConfigFile.properties"
ignore-unresolvable="true"/>
Use on from following solution
<context:property-placeholder
location="classpath:core-application.properties,
classpath:core-services.properties,
classpath:core-messages.properties"
ignore-unresolvable="true"/>
or
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:core-application.properties</value>
<value>classpath:core-services.properties</value>
<value>classpath:core-messages.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>
We can define multiple properties files using XML configuration with this:
<context:property-placeholder location="classpath:foo.properties, ${external.prop.file.path}"/>
If we use JavaConfig wen can use this (Java 8 and onwards):
#PropertySource("classpath:foo.properties")
#PropertySource("${external.prop.file.path}")
public class PropertiesWithJavaConfig {
//...
}
Or this (previous to Java 8):
#PropertySources({
#PropertySource("classpath:foo.properties"),
#PropertySource("${external.prop.file.path}")
})
public class PropertiesWithJavaConfig {
//...
}
BTW, in the event of a property name collision, the last source read takes precedence.
More info here, here and here.
Related
I want to declare a bean of String type in an spring xml context in the shortest way possible. So far this is the shortest way I've found
<bean id="bean1" class="java.lang.String">
<constructor-arg type="String" value="someword" />
</bean>
However, for readability, I would like to 'stack' multiple of these neatly, kinda like so, and I can't just put all the code on one line because of team standards about line code length:
<bean id="bean1" class="java.lang.String">blah blah </bean>
<bean id="bean2" class="java.lang.String">blah blah </bean>
<bean id="bean3" class="java.lang.String">blah blah </bean>
For readability I would still prefer keeping it all in the context file and also I can redeploy the context file separately to the classpath without having to redeploy the whole .jar
If you want to keep everything in the context file and want to simplify it then you should be using PropertyPlaceholderConfigurer instead of String beans.
Here is an example,
Create a properties file named as project.properties.
Put all your Strings inside that file as {key=value} pair. For Example,
myStrings.cnst1=FOO
myStrings.cnst2=BAR
myStrings.cnst3=FOOBAR
Now, declare a PropertyPlaceholderConfigurer inside your context file as follows:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>project.properties</value>
</property>
Now, you can easily access the values as follows:
#Value("${myStrings.cnst1}")
private String someString;
Refer this link and this link for more information.
I have to externalize a couple of links in my application. The links have to be in a properties file which can be changed without the need for a build and deploy. I tried adding values in the server.properties of my jboss and using that variable in my controller but i am unable to get a value.
How do i go about this?
Using ResourceBundle
ResourceBundle bundle = ResourceBundle.getBundle("<myfile>");
String studentName = bundle.getString("<property-name>");
If you put the values in server.properties, then configure PropertyPlaceholderConfigurer in your applicationContext, something like this:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:server.properties</value>
</property>
</bean>
or the shorter Spring 3 version:
<context:property-placeholder location="classpath:server.properties"/>
and then just inject the values you need to your beans with
<bean id="someBean">
<property name="myProperty" value="${this.is.property.from.server.properties}" />
</bean>
or with #Value annotation like
#Value("${this.is.property.from.server.properties}")
private String myProperty;
I have created a spring configuration file that works well.
My next step was to separate user configuration properties from system properties.
I have decided to create additional xml file with beans that will be configured by the user.
I had problem to create few such logical beans encapsulating properties that will be used by real class beans:
I have found over the net an option to reference proprieties in such way:
UserConf.xml
<bean id="numberGuess" class="x...">
<property name="randomNumber" value="5"/>
<!-- other properties -->
</bean>
SystemConf.xml
<import resource="UserConf.xml" />
<bean id="shapeGuess" class="y...">
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/>
<!-- other properties -->
</bean>
But my problem is that i need x... class to be something logical that shouldn't be initialized at all, and i don't want it to disclose any info of the class hierarchy of the system since it should be only in use configuration xml file.
Solution1 is to create a Java object representing this proprieties:
public class MyProps(...)
and add a bean parent in the spring system configuration:
<bean id="MyProps" class="path to MyProps"/>
in the user side change the previous bean to be:
<bean id="numberGuess" parent="MyProps">
<property name="randomNumber" value="5"/>
<!-- other properties -->
</bean>
Solution2 is to use flat configuration file just like Database.props, and load it using factory.
Solution3 is to use Spring Property Placeholder configuration to load properties from XML properties file (e.g. example), but here i simply don't know how to get a more complex nested structure of properties (properties need to be separated by different logical names, e.g. minNumber will be defined both under xAlgo and y algo).
I don't like to create new Java class only to deal with this problem or to move my user configuration to a flat props file (i need the xml structure), is their any other solution??
I will answer my own question, since it looks as the best solution for me (and much more simplistic than was suggested)
I will use PropertiesFactoryBean to do the work for me:
e.g.
UserConf.xml
<bean id="numberGuess" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="randomNumber">3</prop>
<!-- other properties -->
</bean>
SystemConf.xml
<import resource="UserConf.xml" />
<bean id="shapeGuess" class="y...">
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/>
<!-- other properties -->
</bean>
First if you don't know about the property place holder you should take a look at that. Also #Value("${some.property:defaultvalue}") is something you should look at.
Second the word configuration is ambiguous in Spring. Spring uses this word but they mean developer configuration not user configuration. Despite what people say or think Spring is not a configuration engine.
I'm not sure what your trying to do but you should be aware that your configuration will not be adjusted at runtime which is frequently needed for something like "user" configuration. So most people write their own configuration layer.
Another thing you should take a look at is not using the XML configuration and instead use Java Configuration which will give you way more flexibility.
In my Spring xml configuration I'm trying to get something like this to work:
<beans>
<import resource="${file.to.import}" />
<!-- Other bean definitions -->
</beans>
I want to decide which file to import based on a property in a properties file.
I know that I can use a System property, but I can't add a property to the JVM at startup.
Note: The PropertyPlaceHolderConfigurer will not work. Imports are resolved before any BeanFactoryPostProcessors are run. The import element can only resolve System.properties.
Does anyone have a simple solution to this? I don't want to start subclassing framework classes and so on...
Thanks
This is, unfortunately, a lot harder than it should be. In my application I accomplished this by doing the following:
A small, "bootstrap" context that is responsible for loading a PropertyPlaceholderConfigurer bean and another bean that is responsible for bootstrapping the application context.
The 2nd bean mentioned above takes as input the "real" spring context files to load. I have my spring context files organized so that the configurable part is well known and in the same place. For example, I might have 3 config files: one.onpremise.xml, one.hosted.xml, one.multitenant.xml. The bean programmatically loads these context files into the current application context.
This works because the context files are specified as input the the bean responsible for loading them. It won't work if you just try to do an import, as you mentioned, but this has the same effect with slightly more work. The bootstrap class looks something like this:
public class Bootstrapper implements ApplicationContextAware, InitializingBean {
private WebApplicationContext context;
private String[] configLocations;
private String[] testConfigLocations;
private boolean loadTestConfigurations;
public void setConfigLocations(final String[] configLocations) {
this.configLocations = configLocations;
}
public void setTestConfigLocations(final String[] testConfigLocations) {
this.testConfigLocations = testConfigLocations;
}
public void setLoadTestConfigurations(final boolean loadTestConfigurations) {
this.loadTestConfigurations = loadTestConfigurations;
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
context = (WebApplicationContext) applicationContext;
}
#Override
public void afterPropertiesSet() throws Exception {
String[] configsToLoad = configLocations;
if (loadTestConfigurations) {
configsToLoad = new String[configLocations.length + testConfigLocations.length];
arraycopy(configLocations, 0, configsToLoad, 0, configLocations.length);
arraycopy(testConfigLocations, 0, configsToLoad, configLocations.length, testConfigLocations.length);
}
context.setConfigLocations(configsToLoad);
context.refresh();
}
}
Basically, get the application context, set its config locations, and tell it to refresh itself. This works perfectly in my application.
Hope this helps.
For the Spring 2.5 and 3.0, I have a similar solution to louis, however I've just read about 3.1's upcoming feature: property management, which sounds great too.
There is an old issue on the Spring JIRA for adding properties placeholder support for import (SPR-1358) that was resolved as "Won't Fix", but there has since been a proposed solution using an EagerPropertyPlaceholderConfigurer.
I've been lobbying to have SPR-1358 reopened, but no response so far. Perhaps if others added their use cases to the issue comments that would help raise awareness.
Why not:
read your properties file on startup
that will determine which Spring config to load
whichever Spring config is loaded sets specific stuff, then loads a common Spring config
so you're effectively inverting your current proposed solution.
Add something similar to the following:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound"><value>true</value></property>
<property name="locations">
<list>
<value>classpath:propertyfile.properties</value>
</list>
</property>
</bean>
If what you want is to specify the imported XML file name outside applicationContext.xml so that you could replace applicationContext.xml without losing the configuration of the imported XML file path, you can just add an intermediate Spring beans XML file, say, confSelector.xml, so that applicationContext.xml imports confSelector.xml and confSelector.xml only contains an <import> element that refers to the suitable custom beans XML file.
Another means that might be of use are XML entities (defined by adding <!ENTITY ... > elements into the DTD declaration at the beginning of XML). These allow importing XML fragments from other files and provide "property placeholder"-like functionality for any XML file.
Neither of these solutions allows you to have the configuration file in Java's .properties format, though.
André Schuster's answer, which I bumped, helped me solve a very similar issue I was having in wanting to find a different expression of properties depending on whether I was running on my own host, by Jenkins on our build host or in "real" deployment. I did this:
<context:property-placeholder location="file:///etc/myplace/database.properties" />
followed later by
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>WEB-INF/classes/resources/database.properties</value>
...
</list>
</property>
</bean>
which solved my problem because on my development host, I put a link to my own copy of database.properties in /etc/myplace/database.properties, and a slightly different one on the server running Jenkins. In real deployment, no such file is found, so Spring falls back on the "real" one in resources in my class files subdirectory. If the properties in question have already been specified by the file on /etc/myplace/database.properties, then (fortunately) they aren't redefined by the local file.
Another workaround which does not rely on system properties is to load the properties of all the files using a different PropertyPlaceholderConfigurer for each file and define a different placeholderPrefix for each of them.
That placeholderprefix being configured by the initial property file.
Define the first property file: (containing either first or second)
global.properties
fileToUse=first
Define the files containing a property that can be switched depending on the property defined just above:
first.properties
aProperty=propertyContentOfFirst
second.properties
aProperty=propertyContentOfSecond
Then define the place holders for all the files:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:global.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="placeholderPrefix" value="first{" />
<property name="locations">
<list>
<value>classpath:first.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="placeholderPrefix" value="second{" />
<property name="locations">
<list>
<value>classpath:second.properties</value>
</list>
</property>
</bean>
Use the property defined in global to identify the resource to use from the other file:
${fileToUse}{aProperty}
If I add the JVM argument below and have the file myApplicationContext.dev.xml, spring does load
-DmyEnvironment=dev
<context:property-placeholder />
<import resource="classpath:/resources/spring/myApplicationContext.${myEnvironment}.xml"/>
I'm using Spring 3 and load a properties like that:
<context:property-placeholder location="/WEB-INF/my.properties" />
I need to load a specific applicationContext.xml file according to a given system property. This itself loads a file with the actual configuration. Therefore I need two PropertyPlaceHolderConfigurer, one which resolves the system param, and the other one within the actual configuration.
Any ideas how to do this?
Yes you can do more than one. Be sure to set ignoreUnresolvablePlaceholders so that the first will ignore any placeholders that it can't resolve.
<bean id="ppConfig1" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath*:/my.properties</value>
</list>
</property>
</bean>
<bean id="ppConfig2" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="false"/>
<property name="locations">
<list>
<value>classpath*:/myOther.properties</value>
</list>
</property>
</bean>
Depending on your application, you should investigate systemPropertiesMode, it allows you to load properties from a file, but allow the system properties to override values in the property file if set.
Another solution is to use placeholderPrefix property of PropertyPlaceholderConfigurer. You specify it for the second (third, fourth...) configurer, and then prefix all your corresponding placeholders, thus there will be no conflict.
<bean id="mySecondConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="classpath:/myprops.properties"
p:placeholderPrefix="myprefix-"/>
<bean class="com.mycompany.MyClass" p:myprop="${myprefix-value.from.myprops}"/>
Beware -- there might be a bug related to multiple configurers. See http://jira.spring.io/browse/SPR-5719 for more details.
I'm unable to get multiple to work locally... but I'm not yet blaming anyone but myself.
On my own side, playing with PropertyPlaceholderConfigurer both properties :
order (should be lower for first accessed/parsed PPC)
ignoreUnresolvablePlaceholders ("false" for first accessed/parsed PPC, "true" for next one)
and also give 2 distinct id(s) to both PPC (to avoid one to be overwritten by the other)
works perfectly
Hope it helps
You can't do this directly, and this JIRA issue from Spring explains why (check the comment from Chris Beams for a detailed explanation):
https://jira.springsource.org/browse/SPR-6428
However, he does provide a workaround using Spring 3.1 or later, which is to use the PropertySourcesPlaceholderConfigurer class instead of PropertyPlaceholderConfigurer class.
You can download a Maven-based project that demonstrates the problem and the solution from the Spring framework issues github:
https://github.com/SpringSource/spring-framework-issues
Look for the issue number, SPR-6428, in the downloaded projects.
We have the following approach working:
<util:properties id="defaultProperties">
<prop key="stand.name">DEV</prop>
<prop key="host">localhost</prop>
</util:properties>
<context:property-placeholder
location="file:${app.properties.path:app.properties}"
properties-ref="defaultProperties"/>
System property app.properties.path can be used to override path to config file.
And application bundles some default values for placeholders that cannot be defined with defaults in common modules.
Just giving 2 distinct ids worked for me. I am using spring 3.0.4.
Hope that helps.
In case, you need to define two PPC's (like in my situation) and use them independently. By setting property placeholderPrefix, you can retrieve values from desired PPC. This will be handy when both set of PPC's properties has same keys, and if you don't use this the property of ppc2 will override ppc1.
Defining your xml:
<bean name="ppc1"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="ref to your props1" />
<property name="placeholderPrefix" value="$prefix1-{" />
</bean>
<bean name="ppc2"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="ref to your props2" />
<property name="placeholderPrefix" value="$prefix2-{" />
</bean>
Retrieving during Run time:
#Value(value = "$prefix1-{name}")
private String myPropValue1;
#Value(value = "$prefix2-{name}")
private String myPropValue2;