Can I use a placeholder to dynamically add text to a string imported from a property file with Spring's #Value annotation - spring

Does anyone know if I can use placeholders with Spring's #Value annotion?
For example:
#Value("${a.url.from.propertiesFile}")
private void setUrl(String myUrlFromProperties)
{
this.url = myUrlFromProperties;
}
where my properties file would have:
a.url.from.propertiesFile=/firstPartOfUrl{dynamicBitToAddTo}restOfUrl

Yes you can do that.
In this case if you have passed a null value in the parameter then the default value will be taken as defined with the annotation #Value.
But if you are passing a not null value in the parameter then it will not take the default value.
Hope this helps you.

Related

#ConfigurationProperties returns property placeholder instead of null

I have the following #ConfigurationProperties property holder:
#ConfigurationProperties(prefix = "custom.service")
public class CustomServicePropertyHolder {
private String name;
}
and my application.properties looks like this:
custom.service.name=${remote.service.name}
custom.service.....=...
custom.service.....=...
remove.service.name is an environment variable received in the runtime.
However, when remote.service.name was not provided, the value of the EtlConfigurationHolder.name is a string "${remote.service.name}".
How to make the property to return null instead of this placeholder string ?
I also encountered such a problem ... And it has been present for a long time
Unresolved Placeholder Validation for Spring Boot Configuration Properties
https://github.com/spring-projects/spring-boot/issues/4302
https://github.com/spring-projects/spring-boot/issues/1768
I found only one place where at least some solution is attached
https://davidagood.com/spring-boot-fail-on-missing-env-vars/

Is it possible to pass a key as default value in #Value annotation of Spring

I have a situation where we are reading one property from properties file and now we have been asked to point to another endpoint and for some time we have to manage both these endpoints unless this new endpoint is tested and validated throughly.
I wanted to handle this situation by adding this newer property in properties file and in the actual class were we are reading this property with #Value Annotation the old one can be passed as default with its key as value something like
#Value("${backend.endpoint:${older.endpoint}}"). is it possible ?
Yes you can do it, I have tested it, my sample code
code:
#Value("#{ ${spring.myapp.usenewval} ? '${spring.myapp.newval}' : '${spring.myapp.oldval}}'}")
private String message;
Properties
spring:
myapp:
usenewval: false
newval: hello
oldval: world.....
You can always set spring.myapp.usenewval from outside like
java -jar -Dspring.myapp.usenewval=true myapp.jar
You can use it like this. (I've personally never done it, so forgive me if I'm wrong)
#Configuration
public class PropertyConfiguration {
#Value("{'${backend.endpoint:${older.endpoint:}}'}")
private String myValue;
}
This #Value annotation uses backend.endpoint, if it is provided and defaults to older.endpoint, if backend.endpoint is not provided.
If neither is provided, the property must be set null.
There are other ways to handle this as well. Probably, use #Value for both the property and handle in code.
Here is quick fix for you. Kindly refer it.
You can set default value to #Value annotation of spring as following.
#Controller
#RequestMapping(value = "/your path")
public class MyController {
#Value("${key:true}")
private boolean booleanWithDefaultValue;
}
Here, I take Boolean variable and set default value as "true".
Hope this solution works.

How do you make Spring fail fast when using placeholders with #ConfiguraitonProperties and an environment variable is not set?

When using placeholders to externalise configuration in an application.yaml file, and an associated properties class, how do you make sure Spring fails during startup when it can't resolve a placeholder, instead of just using the placeholder itself as the verbatim value?
For example, given this application.yaml file:
example.key: ${MY_ENV_VAR}
and this properties POJO:
#ConfigurationProperties(prefix="example")
public class AcmeProperties {
public String key;
// Getters, setters, constructors omitted...
}
if MY_ENV_VAR is not set on the system, how do you make Spring throw an exception at startup, instead of setting key to literally ${MY_ENV_VAR}?
Note, Spring doesn't return an empty String, which we could force by defining a default with ${MY_ENV_VAR:defaultValue}, or null (SpEL is not evaluated in #ConfigurationProperties, so this can not be defined as the default, and the behaviour of System.getenv("MY_ENV_VAR") returning null in the case of an undefined environment variable isn't mirrored), it literally just uses the placeholder as the value. We'd rather Spring stopped launching the app altogether instead, for all properties in AcmeProperties. Just using #Validated with Hibernate Validator on the classpath doesn't do it, neither does #ConfigurationProperties(prefix="example", ignoreInvalidFields=false) (which is the default anyway).
A manual check for the value containing ${...} could of course be added to all String properties in the POJO, but this is more error-prone, and would also not work if the actual environment variable was set to a string containing that character sequence. Is there a way to check if the placeholder can be resolved, instead of if the value after resolution is complete?
You can have a custom validator which could look like:
object UnresolvedPropertiesValidator : Validator {
override fun supports(clazz: Class<*>): Boolean {
return clazz == String::class.java
}
override fun validate(target: Any, errors: Errors) {
val stringVal = target as String
if (stringVal.startsWith(SystemPropertyUtils.PLACEHOLDER_PREFIX) && stringVal.endsWith(SystemPropertyUtils.PLACEHOLDER_SUFFIX)) {
errors.reject(
"prop.validation",
"Could not resolve placeholder $target"
)
}
}
}
And then create a bean with the name configurationPropertiesValidator:
class UnresolvedPropertiesValidatorAutoConfiguration {
#Bean
fun configurationPropertiesValidator(): UnresolvedPropertiesValidator {
return UnresolvedPropertiesValidator
}
}
As you can see from these https://github.com/spring-projects/spring-boot/blob/24a52aa66ddb92cd14acb2b41d9f55b957a44829/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/EnableConfigurationProperties.java#L47
https://github.com/spring-projects/spring-boot/blob/9630f853be3183f4872428e2e65b6ef4be7a9b7a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBinder.java#L105,
the validator with the name above will be applied after resolving the prop, and it simply ensures the final value doesn't start with ${ and end with }.
This works for us. But hopefully, the issue will be resolved soon, and we will remove this workaround.

Spring Custom Configuration not populating properties with underscore

I am trying to populate Custom class with properties. following is my Custom Properties class:
#Configuration
#ConfigurationProperties(prefix = "foo.bar")
public class CustomProperties{
private String PROPERTY_ONE;
private String propertyTwo;
//setters
//getters
}
and my properties in application.properties are:
foo.bar.PROPERTY_ONE=some text
foo.bar.PROPERTY_TWO=some other text
When I am trying to use value from CustomProperties this is what I gets:
customProperties.getPROPERTY_ONE() = null
customProperties.getPopertyTwo() = some other text
So I observed that if I have variable name with underscore(_) in it not populating the property value.
is there any way to get the value with variable having underscore?
Yes, it is 100% possible to get your configuration values.
It's all about the casing! Inside of CustomProperties simply name your first property propertyOne ... and refactor your getters/setters appropriately ... and you'll be good to go!
Spring's got the camel casing going on when translating the configuration fields to your Configuration classes/properties. So instead of matching the casing of your properties, follow the camel casing equivalent of the property name found in your configuration file.
Example: PROPERTY_ONE translates to propertyOne

Using #Value annotation with Spring and SPeL

I am trying to find a way to do the following in my spring boot 1.5 application.
I have a variable who's value is dynamic meaning it comes in from an external system.
String name = "abc"; //gets set externally
I want to try and use the name's value to lookup my property file and see if there is a matching property defined. something like..
#Value("#{myClassName.name.concat('something')}")
String propertyValue;
Now my application.property file has the following property set
assume name has the value "abc"
property file contents:
abc.something:abcValue
Now, when i try to access the value of the variable propertyValue it gets set to the value abc.something and not abcValue.
I probably think I cannot use #Value with #{} to get to that, I was wondering if there was a way to to use #{} inside ${} so that I goes and fetches the property value after calculating the name of the property using #{}.
Let me know if you need more details please.
A bean life-cycle requires properties to be resolved at compile time. So, #Value requires constant parameter.
You can use Environment bean to access your properties programmatically.
import org.springframework.core.env.Environment;
#Service
public class Serivce {
#Autowired
private Environment environment;
public String getProperty(final String keyPart) {
String key = "build.your." + keyPart;
return environment.getProperty(key)
}
}
By the way you can use #('${spring.some.property}') in SpEL to access placeholder.
// This is valid access to property
#Value("#('${spring.some.property}')")
private String property;

Resources