I have a bean which is populating properties using the #Value annotation like this
#Value("${propbean.value : 'None'}")
private String value;
In my application context I have the following configuration
<bean id="propbean"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="file:${path}/values.properties"
p:ignoreResourceNotFound="true"
p:ignoreUnresolveablePlaceholders="true" />
When the values.properties file is not present, the defaults 'None' are being set as expected, however when the properties file is present, the default values are still being used, even though I receive a log message that the properties file was loaded from the PropertyPlaceholderConfigurer
190315 14.23.44,517 {} {} {} INFO (PropertiesLoaderSupport.java:172) Loading properties file from URL [file:/path/to/file/values.properties]
I need the default values to take effect only when the properties file is missing and/or the placeholders are not resolveable; not all the time. I have also tried using SPEL, but because this is a PropertyPlaceholderConfigurer object as opposed to using the directive the SPEL solution doesn't work for me. It's my understanding that when using the ${value : default} format for placeholders that the default is only substituted when the value is null, however if I remove the " : 'None'" from the value placeholder the property resolves correctly!
Hopefully this helps someone in the future - the problem was the p:ignoreUnresolveablePlaceholders="true" property in the bean definition. Apparently that instructs Spring to ignore the properties being read into the bean altogether no matter what if there is a default value set.
Removing the property/setting to false resolves the problem completely.
Related
I have a Spring web application with an applicationContext.xml file in the WEB-INF directory. In that file I have the following definitions:
<context:property-placeholder location="file:/etc/mycompany/myapp.properties"
order="-1" ignore-unresolvable="true" ignore-resource-not-found="true" />
<context:property-placeholder location="classpath:myapp-defaults.properties"
order="0" ignore-unresolvable="true" ignore-resource-not-found="true" />
<context:component-scan base-package="com.mycompany" />
<context:annotation-config/>
Then I have a class, com.mycompany.MyClass:
#Named
#Singleton
#Service("myClassService")
public class MyClass {
private static final Logger logger = LoggerFactory.getLogger(MyClass.class);
#Value("${myprop}")
private String myProp;
}
I have two properties files, myapp.properties (located in the classpath) and /etc/mycompany/myapp.properties. Both of these define the myprop property with different values.
If I comment out both of the elements in my applicationContext.xml file and print out the value of the myProp variable, I get the value "${myprop}" (this is expected).
However, now for the weird part. If I just comment out one of the lines I always get the value from the classpath properties file. Note that it doesn't matter which property-placeholder line I comment out. Even if I comment out the one including the classpath properties definition, I still get the classpath properties value into the variable.
If I use a property that is only defined in the filesystem properties file I get the correct value.
So in other words, there seems to be no way for me to override the values in myapp-defaults.properties (in the classpath) by defining them in the properties file on my filesystem. The only way to get properties from a file on the filesystem is if that property is not defined in the classpath properties file.
I have tried reversing the order attribute to no avail (as I understand, this is the correct way, the lower number should have precedence).
I also tried debugging my application and put a breakpoint at select places in the Spring source code. I was able to see that the properties were being loaded from the filesystem properties file. When I commented out the classpath property-placeholder I noted that the filesystem property file was being loaded correctly but when the variable value was printed out it still had the value defined in the classpath properties file.
I'm really confused here. Is there some pitfall I'm falling into here?
I have a Spring 3.1 application. Let's say it has an XML with the following content:
<context:property-placeholder location="classpath:somename.properties" />
<context:property-placeholder location="classpath:xxx.properties" />
I would like some.properties to be always loaded (let's assume it exists), but the xxx part of the second place holder to be replaced by some name depending on the active profile. I've tried with this:
<beans profile="xx1">
<context:property-placeholder location="classpath:xx1.properties" />
</beans>
<beans profile="xx2">
<context:property-placeholder location="classpath:xx2.properties" />
</beans>
Also, both files have properties with the same key but different value.
But it didn't work as some later bean that has a placeholder for one property whose key is defined in xx1.properties (and xx2.properties) makes Spring complain that the key is not found in the application context.
You can do:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
It works fine, but is perhaps not adapted when using multiple profiles in the same time.
When declaring 2 property placeholders, if the 1st one does not contain all the applications keys, you should put the attribute ignoring unresolvable = true, so that the 2nd placeholder can be used.
I'm not sure if it is what you want to do, it may if you want both xx1 and xx2 profiles be active in the same time.
Note that declaring 2 propertyplaceholders like that make them independant, and in the declaration of xx2.properties, you can't reuse the values of xx1.properties.
If you need something more advanced, you can register your PropertySources on application startup.
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
file you create:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String profile = System.getProperty("spring.profiles.active");
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
Once you've done it you just need to add in your context:
<context:property-placeholder/>
Imho it's the best way to deal with spring properties, because you do not declare local properties everywhere anymore, you have a programmatic control of what is happening, and property source xx1 values can be used in xx2.properties.
At work we are using it and it works nicely. We register 3 additional property sources:
- Infrastructure: provided by Puppet
- Profile: a different property loaded according to the profile.
- Common: contains values as default, when all profiles share the same value etc...
I have decided to submit and answer to this as it has not yet been accepted. It may not be what you are looking for specifically but it works for me. Also note that i am using the new annotation driven configuration however it can be ported to the xml config.
I have a properties file for each environment(dev.properties, test.properties etc)
I then have a RootConfig class that is the class that is used for all the configuration. All that this class has in it is two annotations: #Configuration and #ComponentScan(basePackageClasses=RootConfig.class).
This tells it to scan for anything in the same package as it.
There is then a Configuration Containing all my normal configuration sitting wherever. There is also a configuration for each environment in the same package as the root configuration class above.
The environment specific configurations are simply marker classes that have the following annotations to point it to the environment specific properties files:
#Configuration
#PropertySource("classpath:dev.properties")
#Import(NormalConfig.class)
#Profile("dev")
The import tells it to bring in the normal config class. But when it gets in there it will have the environment specific properties set.
I am trying to use #Value annotation in the parameters of a constructor as follows:
#Autowired
public StringEncryptor(
#Value("${encryptor.password:\"\"}") String password,
#Value("${encryptor.algorithm:\"PBEWithMD5AndTripleDES\"}") String algorithm,
#Value("${encryptor.poolSize:10}") Integer poolSize,
#Value("${encryptor.salt:\"\"}") String salt) {
...
}
When the properties file is present on the classpath, the properties are loaded perfectly and the test executes fine. However when I remove the properties file from the classpath, I would have expected that the default values would have been used, for example poolSize would be set to 10 or algorithm to PBEWithMD5AndTripleDES however this is not the case.
Running the code through a debugger (which would only work after changing #Value("${encryptor.poolSize:10}") Integer poolSize to #Value("${encryptor.poolSize:10}") String poolSize as it was causing NumberFormatExceptions) I find that the defaults are not being set and the parameters are in the form of:
poolSize = ${encryptor.poolSize:10} or
algorithm = ${encryptor.algorithm:"PBEWithMD5AndTripleDES"}
rather than the expected
poolSize = 10 or
algorithm = "PBEWithMD5AndTripleDES"
Based on SPR-4785 the notation such as ${my.property:myDefaultValue} should work. Yet it's not happening for me!
Thank you
Perhaps initialization of property placeholder configurer fails due to missed properties file, so that placeholders are not resolved. You can configure it to ignore missed files as follows (if you use context namespace to configure it):
<context:property-placeholder ignore-resource-not-found="true" ... />
Also you don't need "..." around default values.
ignore-resource-not-found="true" is not necessary for the defaults to be picked up. The point of specifying the default value is for it to be used if the property is not found anywhere.
I think the last sentence in the previous answer points to the problem - incorrect EL that you must have originally provided but then removed from the example. The fact that you were getting format conversion exceptions points to that as well. Normally, Spring will automatically convert Strings to the appropriate "standard" Java type, and if you provide your own implementation of the Spring Conversion Service, to your custom objects as well - as long as your conversion service is defined in the app context.
"ignore-resource-not-found" is useful when you are injecting properties via XML without defaults and don't want the container to throw an exception instantiating the bean in case no property is found. In such cases the bean properties will be initialized with the Java defaults, e.g. nulls fro objects, 0s for primitive numeric values, etc.
In my case, resolving the property values (and the defaults) did not work in test, where I use an annotation based configuration. It turned out that I had to add a PropertySourcesPlaceholderConfigurer so that properties actually get resolved. It was explained in the PropertySource Annotation JavaDoc:
In order to resolve ${...} placeholders in definitions or #Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer. This happens automatically when using in XML, but must be explicitly registered using a static #Bean method when using #Configuration classes. See the "Working with externalized values" section of #Configuration Javadoc and "a note on BeanFactoryPostProcessor-returning #Bean methods" of #Bean Javadoc for details and examples.
The following did the trick:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
And if you want to add individual properties:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
Properties properties = new Properties();
properties.put("batchSize", "250");
propertySourcesPlaceholderConfigurer.setProperties(properties);
return propertySourcesPlaceholderConfigurer;
}
Is it possible to set a Spring bean sub-property using dot notation? For instance:
<bean name="rememberMe" class="com.mydomain.security.RememberMeManager">
<property name="cookie.domain" value=".${webRoot}"/>
</bean>
Or do I need to also create an intermediary bean for the Cookie object stored in RememberMeManager.getCookie()?
My objective is to set cookies set by my site to ".mydomain.com" instead of "mydomain.com". I have a properties file with webRoot=mydomain.com in it.
Spring's PropertyPlaceholder will have no problem with replacing placeholders that are substrings of the property/value, such as ".${webRoot}", and according to the documentation, it will also fallback to the system properties if no property in the properties file is found.
Did you try this? Does it work or not?
I have an ApplicationContext.xml file with the following node:
<context:property-placeholder
location="classpath:hibernate.properties, classpath:pathConfiguration.properties" />
It specifies that both properties files will be used by my application.
Inside pathConfiguration.properties, some paths are defined, such as:
PATH_ERROR=/xxx/yyy/error
PATH_SUCCESS=/xxx/yyy/success
A PathConfiguration bean has setters for each path.
The problem is: when some of those mandatory paths are not defined, no error is thrown. How and where should I handle this problem?
The standard behaviour of the PropertyPlaceholder that is configured via <context:property-placeholder ... /> throws an exception when a property cannot be resolved once it is required in some place as long as you do not configure it otherwise.
For your case if you have a Bean that requires some properties like this, it will fail when the value cannot be resolved. For example like this:
public class PropertiesAwareBean {
#Value("${PATH_ERROR}")
private String errorPath;
String getErrorPath() {
return errorPath;
}
}
If you want to relax the PropertyPlaceholder and don't make it throw an Exception when a property cannot be resolved you can configure the PropertyPlaceholder to ignore unresolvable properties like this <context:property-placeholder ignore-unresolvable="true" ... />.
One way to reinforce the verification of parameters is to switch to a classical PropertyPlaceholderConfigurer bean in your beans file.
The PropertyPlaceholderConfigurer has properties which you can use to tweak its behavior and specify either an exception is thrown or not if some key is missing (take a look at setIgnoreUnresolvablePlaceholders or setIgnoreResourceNotFound).
If I remember correctly, in Spring 2.5, only the location attribute is supported for <context:property-placeholder> (things might have changed though).
I'm not sure if I fully understand your issue, but there are probably a variety of ways to approach this. One would be to make the paths mandatory by using constructor injection. In the constructor you could then validate the incoming values and if null for example, throw BeanInitializationException instances.