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

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.

Related

Spring Boot & Vault: incomplete context initialization issue

I've faced an issue that on rare occasions (it might take dozens of restarts) Spring doesn't initialize all properties correctly.
I define the bean of CbKafkaConsumerConfig (my custom bean) type and check its state in the thread that was created by a method that is marked as #EventListener(ApplicationReadyEvent.class), so I expect it to be completely initialized by this point. However, this is what I see:
Values that I expected to be filled are left with placeholders.
Here's how they are defined in application.properties file. (And I've checked the spelling - it's correct, otherwise it would fail every time, not occasionally)
config-bean-prefix.msg-topics=${cb.kafka.tc-topic}
config-bean-prefix.unexpected-error-topic=${cb.kafka.unexpected-errors-topic}
These properties are defined in Vault and I expected them to be fetched and set with the power of Spring Cloud Vault. Here you can see that Vault is present as a property source AND that these properties are populated there.
At the same time, in the context there are other beans of the same type CbKafkaConsumerConfig that are referring to these properties and yet it resolved fine for them.
Here's how the bean is defined
#Bean({"myBean"})
#ConfigurationProperties(
prefix = "config-bean-prefix"
)
public CbKafkaConsumerConfig myBeanConsumer() {
return new CbKafkaConsumerConfig();
}
And the bean itself:
#Data
public class CbKafkaConsumerConfig extends CbKafkaBaseConfig {
#NotNull
#Size(
min = 1
)
private Collection<String> msgTopics;
#NotNull
private String unExpectedErrorTopic;
}
We're using Spring Boot 2.2.x however this issue is also present for Spring Boot 2.1.x.
It's not specific for this type of beans, other might fail as well while being correctly set in Vault. What could be the reason of such unpredictable behavior and what I should look into?
Turns out by default spring cloud vault is not simply fetching properties on start, every so often it's updating them. While updating, there's a short time window when properties were already deleted from property source in the context, but not filled with the new ones and it might actually happen during context initialization (super questionable behavior in my opinion) causing some beans being corrupted.
If you don't want properties to be updated in runtime just set spring.cloud.vault.config.lifecycle.enabled to false

#Value Integer variable in Spring Controller Causes a jUnit Error (jUnit does not use this variable)

I have a strange error in my jUnit for a SpringMVC Controller which has a #Value Integer class variable:
#Value("${max.insert.participants.batch.size}")
private Integer maxInsertParticipantsBatchSize;
The setting comes from a Properties file (all other settings are also read properly from it)
max.insert.participants.batch.size=1000
This is a variable from a Properties file. It is not used or checked in the jUnit, but the jUnit fails with:
12:38:21 Caused by: java.lang.NumberFormatException: For input string: "${max.insert.participants.batch.size}"
12:38:21 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[?:?]
As soon as I change the #Value type to a String or a Boolean the jUnit starts working. For instance
#Value("${max.insert.participants.batch.size}")
private String maxInsertParticipantsBatchSize;
Once again, the jUnit does not use this variable. It is only used in the actual application.
If your test case is a unit test then this will not work. As the spring context does not get initialized with the unit test. The way you can use the value of this variable in the unit test is by setting the variable value like this.
ReflectionUtils.setField(fieldName, object, value);
If your test case is a component test case where spring context is getting initialized then you need to supply the value in your application property file like this
max.insert.participants.batch.size:3
you can also provide a default value like this
#Value("${max.insert.participants.batch.size:3}")
private Integer maxInsertParticipantsBatchSize;
The default value will be overwritten with the value you provide in the application property file.

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 do I set Spring properties in run configuration in Intellij

I have the following code...
#Service
public class PropertiesService {
...
#Value("external.config.active") private String useExternalConfig
So in intellij I set the VM Options to...
-Dexternal.config.active=true
But when I debug in the application this.useExternalConfig.equals("external.config.active") is true.
What do I have to do to set a Spring property in the run configuration for IJ
Update I see it being supplied in the java command...
/.../java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:53192,suspend=y,server=n -Dexternal.config.active=true -javaagent:/.../Caches/IdeaIC2018.1/groovyHotSwap/gragent.jar -javaagent:/.../Caches/IdeaIC2018.1/captureAgent/debugger-agent.jar=file:/private/var/folders/3d/5f6dvvs573zg3ydvxbd0b0h40000gn/T/capture2.props -Dfile.encoding=UTF-8 -classpath
#Value takes a value expression ${...} or a SpEL expression #{...} as you haven't provided any of those the value as is will be used. To substitute a property you can use a value expression ${name.of.property}.
Or if you really like hardcore you can use SpEL #{#environment.getProperty('name.of.property')}. You see the value expression is easier.
This does seem to work...
#Value("${external.config.active}")
based on...
The actual value expression: e.g. "#{systemProperties.myProp}".
This is strange so if anyone can explain it further they get the check.

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}

Resources