Spring ConfigurationProperties validation of a Double value - spring-boot

I have a property value that should range between 0 and 1.
I like Spring's ConfigurationProperties to validate the property value.
So in my ConfigProperties class I added the #Validated annotation and wrote this:
#Min(0)
#Max(1)
Double fraction;
The strange thing is that the validation works in a manner that looks like flooring / roofing the value from the property file.
This is the outcome of different values I put in the conf file:
fraction=-2.1 -> Spring reports an error and stops (good!)
fraction=2.1 -> Spring reports an error and stops (good!)
fraction=-1.5 -> Spring doesn't report an error and starts (not good!)
fraction=1.5 -> Spring doesn't report an error and starts (not good!)
I also tried using the #Range annotation, but with the same outcomes

So here is the solution as described here:
#DecimalMax("1.0") #DecimalMin("0.0")
Double fraction;

As (clearly) stated by the documentation of both #Min and #Max. The same applies to #DecimalMin and #DecimalMax
Note that double and float are not supported due to rounding errors (some providers might provide some approximative support)
You can use a BigDecimal or BigInteger instead.

Related

How to retrieve full endpoint URL in Quarkus test?

I am looking a solution to retrieve full endpoint URLs within Quarkus application to use it in tests to avoid hard-coding the paths.
The official guide suggests using the #TestHTTPEndpoint and #TestHTTPResource annotations.
If I annotate my test class with #TestHTTPEndpoint(MyResource::class), then all calls via RestAssured without specifying the path work just fine. The problem is, when I try to retrieve the endpoint URL like this (let's say, I need to call multiple endpoints in one test):
#TestHTTPEndpoint(MyResource::class)
#TestHTTPResource
lateinit var myResourceUrl: URL
it kind of works, but the injected URL does not include the quarkus.http.root-path value.
Instead of http://localhost:8081/root-path/my-resource I get just http://localhost:8081/my-resource.
Is there a way to retrieve a full endpoint path that includes the quarkus.http.root-path value?
Introduction
Let's consider the following versions as the current versions.
Quarkus: 2.11.2.Final.
Root cause analysis
The io.quarkus.test.common.http.TestHTTPResourceManager (TestHTTPResourceManager for short) class performs the value injection into the fields annotated with the io.quarkus.test.common.http.TestHTTPEndpoint (TestHTTPEndpoint for short) annotation:
The TestHTTPResourceManager class retrieves an instance of the org.eclipse.microprofile.config.Config (Config for short) from the org.eclipse.microprofile.config.ConfigProvider (ConfigProvider for short) class and uses the test.url configuration property value retrieved from the Config instance (the Config.getValue(…) method call) as the base URL.
The retrieved test.url configuration property value seems to correspond to the test.url system property value that was provided by the io.quarkus.test.common.http.TestHTTPConfigSourceProvider (TestHTTPConfigSourceProvider for short) class.
Possible root cause
For some reason the TestHTTPConfigSourceProvider class does not take into account the quarkus.http.root-path property value, when providing the test.url system property value, which seems to be used as the base URL: quarkus/TestHTTPConfigSourceProvider.java at 2.11.2.Final · quarkusio/quarkus:
static final String TEST_URL_VALUE = "http://${quarkus.http.host:localhost}:${quarkus.http.test-port:8081}${quarkus.servlet.context-path:}";
static final String TEST_URL_KEY = "test.url";
Therefore, it looks like a Quarkus issue: a defect (a bug) or a lack of a feature (or a lack of the feature implementation).
Therefore, it is worth reporting it as a Quarkus issue.
OK, so it seems to be a bug in Quarkus and will be fixed soon.
#TestHTTPResource annotation injects endpoint URL without the quarkus.http.root-path segment · Issue #27416 · quarkusio/quarkus · GitHub.
As a workaround, one could set the quarkus.servlet.context-path property in the test/resources/application.properties file like this:
quarkus.servlet.context-path=${quarkus.http.root-path}

Spring Boot | spring EL using with annotation attribute value to set any random value using properties

I am using one existing spring boot annotation and want to set the attribute value dynamically using spring expression and want to have one constant value as a prefix,
sample Code snippet
#KafkaListener(topics="${topic.name}", groupId="#{'consumergroup' + (int)(java.lang.Math).random() }")
public void consumerMethod(){
}
I want to create a new consumer group for each new container using the above kind of implementation.
But I am facing the below exception.
Expression parsing failed; nested exception is
org.springframework.expression.spel.SpelParseException: EL1041E: After
parsing a valid expression, there is still more data in the
expression: 'lparen(()
kindly help me to either use template spring EL or any other way I can use to set dynamic consumer group id with the constant prefix.
groupId="#{'consumergroup' + (100 * T(Math).random()).intValue() }"
There is no cast operator in SpEL. Just because it uses a ConversionService internally to convert from one type to another. I use intValue() any way because the result of random() is Double (not double) - SpEL does coercion into type wrappers for API convenience.
The java.lang is imported into SpEL context automatically. No need to add it for Math type.
100 *. See Math.random() JavaDocs: it returns the value between 0.0 and 1.0. So, casting to int would always bring you only 0. The casting doesn't do rounding.
Also see Spring Boot configuration properties feature where you can use random value for the groupId:
spring.kafka.consumer.group-id=consumergroup${random.int}
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.random-values

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.)

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

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}

Spring #Value default property is not taken when runing JUnit tests

This is confusing. I have a property outerParameter, which is optionaly given among VM options when starting tomcat. I am using it by the following way in my logic:
#Value("${outerParameter:paused}")
private String featureStatus = "active";
public String getFeatureStatus() {
return featureStatus;
}
When starting tomcat without parameter - getFeatureStatus gives "paused", as expected. When starting with defined parameter - gives this parameter value, as expected.
The confusing part is that when I am runing JUnit tests for getFeatureStatus, it anyway gives me "active" and not the default "paused". The context for tests doesn't contain any <context:property-placeholder../> configuration.
I am trying to understand what I am missing, maybe somebody could give me a hand
I found this:
Spring #Value annotation not using defaults when property is not present
which could be the answer for my case too. It says "Perhaps initialization of property placeholder configurer fails due to missed properties file, so that placeholders are not resolved".
But if so, why it doesn't fail when starting tomcat without defined outerParameter?
Thanks
It means that the property is not loaded in the test case's classpath. Try loading the properties file in the context for test.

Resources