How to set Spring camel case property with uppercase environment variable? - spring

I have some code to load a value as such in my Spring application:
#Component
public class MyElasticRestService {
#Value("${elasticApi.baseURL}")
private String elasticApiBaseUrl;
According to the Spring docs, I should be able to use a relaxed binding that comes from an uppercase environment variable such as ELASTIC_API_BASE_URL or ELASTICAPI_BASEURL. But I'm confused which is correct. Both don't seem to work so I am wondering how to debug what is actually picked up.
I've loaded Spring Boot Actuator to view the configprops endpoint. But it doesn't have anything on the elasticApi prefix.
What should the correct environment variable be and how can I see how it gets translated and picked up by the application?

The #Value annotation doesn't support relaxed bindings. Therefore you could use a class annotated with #ConfigurationProperties or you use a RelaxedPropertyResolver to get the value from the environment.

According to https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-vs-value, it is now very possible simply with #Value as long as you use kebab-case (all lower case with dash) for the name e.g. #Value("config.refresh-rate")

Instead of trying to make it an UPPER_SNAKE_CASE, you can put it in your application.yaml file, this way:
elasticApi.baseURL: ${ELASTIC_API_BASE_URL:defaultvalue}
or this way doesn't really matter:
elasticApi:
baseURL: ${ELASTIC_API_BASE_URL:defaultvalue}

Related

Reading a configuration Value from YAML in Micronaut

How to read a value from application.yml in my Micronaut project? I can clearly see annotation is resolved to proper value (true in this case), but it is not applied to the variable (stays as default false). I've tried using #Value and #ConfigurationProperties
In a comment the OP has indicated that they are doing new FeatureToggleService(). Creating your own instance of the object is the problem. Instead of using new, let the DI container create and manage the instance. If you do, then #Value will be relevant.
See https://github.com/jeffbrown/filiard/blob/f6f704fb95d7821919748bb41968f87d11cee07b/src/main/java/filiard/DemoController.java and https://github.com/jeffbrown/filiard/blob/f6f704fb95d7821919748bb41968f87d11cee07b/src/main/java/filiard/FlagHelper.java for a working example.
UPDATE:
Based on additional information this is not the correct answer!!!
As pointed out, #Value can be private, but Micronaut advices against it.
Short answer, it is because it is private. Wrong
From the documentation:
The #Value annotation accepts a string that can have embedded placeholder values (the default value can be provided by specifying a
value after the colon : character). Also try to avoid setting the
member visibility to private, since this requires Micronaut Framework
to use reflection. Prefer to use protected.
Also, consider using #Property instead of #Value. Still valid
https://docs.micronaut.io/latest/guide/#valueAnnotation
NOTE:
The Micronaut framework does not inspect a manually created instance, even if it is instantiated in a #Factory, unlike other frameworks.

When using ConfigurationProperties with a camelCase prefix, how do I solve "Prefix must be in canonical form"?

I had a property configured in my yml as
foobar:
baz: 7
and a configuration class annotated with
#ConfigurationProperties(prefix = "foobar")
and everything was working fine.
The code in my organization is generally camelCase, so I renamed both the property and prefix to fooBar. IntelliJ is now highlighting the prefix = "foobar" line with the error, "Prefix must be in canonical form". What can I do, while keeping camelCasing in the yml config?
Spring Boot supports multiple formats of property names, but encourages you to access them in a canonical way.
Per Property Binding in Spring Boot 2.0:
It turns out the idea of relaxed property names is much easier to implement if you restrict it to one direction. You should always access properties in code using a canonical form, regardless of how they are represented in the underlying source.
The ConfigurationPropertyName class enforces these canonical naming rules, which basically boil down to “use lowercase kebab-case names”.
So, for example, you should refer to a property in code as person.first-name even if person.firstName or PERSON_FIRSTNAME is used in the underlying source.
You can keep your config yml in camel case:
fooBar:
baz: 7
but change the access in the configuration class annotation to use kebab-case:
#ConfigurationProperties(prefix = "foo-bar")

Kotlin Spring boot #Value annotation process

#Value("\${datasource.host}")
private val host: String = ""
I wrote the following code in KOTLIN and it worked fine.
I don't understand how the host was injected into the host.
In my knowledge, the value should not be injected because the host variable is val.
How does this code work?
Short answer: Spring is magical!
For a Kotlin property, val doesn't necessarily mean that the property is constant.  (It's not an exact equivalent of Java final here.)  It simply means that there's a get() method but no set() method.
That leaves open the possibility for the value to change some other way.  (For example, the property could have a custom getter which returned different values.)
I'm not sure quite how Spring works its magic; it may be able to set the property's backing field directly, or it may create a hidden subclass which can.  In any case, it's perfectly capable of setting val properties.  (You can also see this in Hibernate.)

Optional environment variables in Spring app

In my Spring Boot app's application.properties I have this definition:
someProp=${SOME_ENV_VARIABLE}
But this is an optional value only set in certain environments, I use it like this
#Value("${someProp:#{null}}")
private String someProp;
Surprisingly I get this error when the env. var doesn't exist
Could not resolve placeholder 'SOME_ENV_VARIABLE' in string value "${SOME_ENV_VARIABLE}"
I was expecting Spring to just set a blank value if not found in any PropertySource.
How to make it optional?
Provide a default value in the application.properties
someProp=${SOME_ENV_VARIABLE:#{null}}
When used like #Value("${someProp}), this will correctly evaluate to null. First, if SOME_ENV_VARIABLE is not found when application.properties is being processed, its value becomes the string literal "#{null}". Then, #Value evaluates someProp as a SpEL expression, which results in null. The actual value can be verified by looking at the property in the Environment bean.
This solution utilizes the default value syntax specified by the PlaceholderConfigurerSupport class
Default property values can be defined globally for each configurer
instance via the properties property, or on a property-by-property
basis using the default value separator which is ":" by default and
customizable via setValueSeparator(String).
and Spring SpEL expression templating.
From Spring Boot docs on externalized configuration
Finally, while you can write a SpEL expression in #Value, such
expressions are not processed from Application property files.
This work for me:
spring.datasource.url=jdbc:mysql://${DB_IP:localhost}:3306/app
spring.datasource.username=${SPRING_DATASOURCE_USERNAME:mylocaluser}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:localpass}
Because this is probably interesting to others who come here, you can override any properties file w/ an env variable implicitly. Let's say you have property.
someapp.foo
Then you can define an env variable SOMEAPP_FOO (capital letters and . -> _ ) and spring will implicitly set the property from the env. variable.
Described further here: https://hughesadam87.medium.com/how-to-override-spring-properties-with-env-vars-82ee1db2ae78

org.springframework.core.env.Environment.getProperty(String) returns null

I am trying to access some properties programmatically in a controller of a spring mvc application. I configured it by xml. I tried both PropertyPlaceholderConfigurer and <context:property-placeholder />
I tried to use in the controller class(saw it in a working example but it was configured with #Configuration):
#Inject
private Environment environment;
and afterwards i use:
environment.getProperty("upload.location")
but i get a null value. The entry exists in the properties file(i have only one) and also using ${...} in the xml works
A much simpler way - use #Value to inject the system property, as follows:
private #Value("${systemPropertyFoo}") String systemPropertyFoo;
In your case (I"m assuming the variable is a system property):
private #Value("${upload.location}") String uploadLocation;
This annotation depends on the PropertyPlaceholderConfigurer, so keep it in your config.

Resources