How to interpolate property values provided by custom PropertySource in Spring Boot? - spring-boot

I have my custom FooPropertySources that extends EnumerablePropertySource. I add all of these in the #Configuration class to the ConfigurableEnvironment and they are correctly picked up be application and all the values are resolved.
However, if some values contain placeholders, they're not being interpolated. I thought I should use PropertySourcesPlaceholderConfigurer to solve that problem, but it seems like this configurer is meant to deal with placeholders in beans, rather than in property sources.
So far I tried this:
#Configuration
#ConditionalOnProperty("foo.config.import")
open class FooConfiguration {
#Autowired
private lateinit var env: ConfigurableEnvironment;
#Value("\${foo.config.import}")
private lateinit var locationSpecifier: String;
#PostConstruct
private fun initialize() {
val placeholderConfigurer = PropertySourcePlaceholderConfigurer();
val beanFactory = DefaultListableBeanFactory();
this.resolvePropertySources(this.parseLocationSpecifier())
.forEach(this.env.propertySources::addFirst);
placeholderConfigurer.setEnvironment(this.env);
placeholderConfigurer.postProcessBeanFactory(beanFactory);
}
internal fun resolvePropertySources(path: Path): Set<FooPropertySource> {
//...
return ...;
}
internal fun parseLocationSpecifier(): Path {
//...
return path;
}
}
Now, if an instance of FooPropertySource contains these properties:
firstname = John
lastname = Doe
fullname = ${firstname} ${lastname}
I'd like, in the end, when my application calls to env.getProperty("fullname") it will get the string "John Doe", rather than "${firstname} ${lastname}".
Any hopes to resolve that problem? I'm struggling with it for third day already… :-(

I guess you could create an extension function
fun ConfigurableEnvironment.fullname() = "${getProperty("firstname")} ${getProperty("lastname")}"

Related

Spring #Value not working in Spring Boot 2.5.5, getting null values

I am trying to inject some property values into variables by means of Spring #Value annotation but I get null values. I tried different configurations and triks but it doesn't work. Think is that before today everythink was working properly. I do not know what I changed in order to get things broken.
Here is my java class:
#Component
#ConditionalOnProperty(prefix = "studioghibli", name = "get")
public class StudioGhibliRestService {
#Value("${studioghibli.basepath}")
private static String BASE_PATH;
#Value("${studioghibli.path}")
private static String PATH;
#Value("${studioghibli.protocol:http}")
private static String PROTOCOL;
#Value("${studioghibli.host}")
private static String HOST;
private static String BASE_URI = PROTOCOL.concat("://").concat(HOST).concat(BASE_PATH).concat(PATH);
#Autowired
StudioGhibliRestConnector connector;
public List<StudioGhibliFilmDTO> findAllFilms() throws SipadContenziosoInternalException {
var response = connector.doGet(BASE_URI, null, null);
if (!response.getStatusCode().is2xxSuccessful() || !response.hasBody()) {
throw new SipadContenziosoInternalException(Errore.INTERNAL_REST_ERROR, "FindAll(), microservizio ".concat(BASE_URI), null);
}
return (List<StudioGhibliFilmDTO>) response.getBody();
}
}
As you can see, the class is annotated with #Component, that because I will need to use it as #Service layer in order to make a rest call in my business logic.
The class is also annotaded with conditional on property...
Here is a screenshot of the debug window at startup:
Since the PROTOCOL value is null, i get a null pointer exception immediately at start up.
Here is part of the application-dev.properties file:
studioghibli.get
studioghibli.protocol=https
studioghibli.host=ghibliapi.herokuapp.com
studioghibli.basepath=/
studioghibli.path=/films
First of all, #Value annotation does not work with static fields.
Secondly, fields with #Value annotation is processed when the instance of the class (a bean) is created by Spring, but static fields exist for a class (for any instance), so when the compiler is trying to define your static BASE_URI field other fields are not defined yet, so you get the NPE on startup.
So you might need a refactoring, try to inject values with the constructor like this:
#Component
#ConditionalOnProperty(prefix = "studioghibli", name = "get")
public class StudioGhibliRestService {
private final StudioGhibliRestConnector connector;
private final String baseUri;
public StudioGhibliRestService(StudioGhibliRestConnector connector,
#Value("${studioghibli.basepath}") String basePath,
#Value("${studioghibli.path}") String path,
#Value("${studioghibli.protocol:http}") String protocol,
#Value("${studioghibli.host}") String host) {
this.connector = connector;
this.baseUri = protocol.concat("://").concat(host).concat(basePath).concat(path);
}
// other code
}
Thanks, It works for me, I have to add some codes to my project. Then I check the spring core document in "#Value" section. Besides
When configuring a PropertySourcesPlaceholderConfigurer using
JavaConfig, the #Bean method must be static.
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}

Testing Kotlin Extension Functions with Spring

I have a controller that gets a specific service based on a customer name:
#RestController
class BasicController(){
#Autowired
private lateinit var services: List<BasicService<*>>
private var service: BasicService<*>? = null
#GetMapping("/{customer}")
fun getAll(#PathVariable customer: String): ResponseEntity<String>{
service = services.getServiceByCustomer(customer)
/... code w/return value .../
}
}
I have a file Extensions.kt with the following:
fun <T: BasicService> List<T>.getServiceByCustomer(customer: String): T?{
return this.find{
it::class.simpleName?.contains(customer, ignoreCase = true) == true
}
}
Is it possible to return a mock of service when services.getServiceByCustomer is called similar to `when`(mock.function(anyString())).thenReturn(value)?
I've tried using mockK with the following:
mockkStatic("path.to.ExtensionsKt")
every {listOf(service).getServiceByCustomer)} returns service
But I don't think I'm using that properly... I'm currently using com.nhaarman.mockitokotlin2 but have tried io.mockk
You just need to use a customer that actually matches the simple name of the mocked service. You don't need or even should mock the extension function. Try the following:
class BasicControllerTest {
#MockK
private lateinit var basicService: BasicService
private lateinit var basicController: BasicController
#BeforeEach
fun setUp() {
clearAllMocks()
basicController = BasicController(listOf(basicService))
}
}
Additionally, consider using constructor injection instead of field injection:
#RestController
class BasicController(private val services: List<BasicService<*>>){
private var service: BasicService<*>? = null
#GetMapping("/{customer}")
fun getAll(#PathVariable customer: String): ResponseEntity<String>{
service = services.getServiceByCustomer(customer)
/... code w/return value .../
}
}
Finally, consider testing the Controllers with #WebMvcTest instead of regular unit tests. Check more info here https://www.baeldung.com/spring-boot-testing#unit-testing-with-webmvctest.

#Value not set in one specific class

I'm fairly sure I'm being some kind of idiot, but for the life of me I can't see it.
I have a large Spring Boot 2.1 application that extensively uses injection of properties through the #Value annotation. This works great, has done for years. But there's one specific, brand-new object where I can't get the values set. They are always null.
I know the problem isn't with the values themselves, because some of the same values inject just fine into other objects. But I just can't see what's wrong with THIS object, and would be grateful for your eyeballs.
The values in this object (which is in the same directory and builds just fine) are always null:
#Service
public class SSOUtil {
private String domain = "https://login.microsoftonline.com/";
private String tenantId = "[deleted guid]";
public static String localEnvironment = "local";
public static String devEnvironment = "dev";
public static String testEnvironment = "test";
public static String prodEnvironment = "prod";
#Value("${actions.PROD.touchnet_azure_ad_client_secret}")
private String clientSecretTouchnetProd;
#Value("${actions.TEST.touchnet_azure_ad_client_secret}")
private String clientSecretTouchnetTest;
#Value("${actions.DEV.touchnet_azure_ad_client_secret}")
private String clientSecretTouchnetDev;
#Value("${actions.touchnet_azure_ad_client_id_dev}")
private String clientIdDev;
#Value("${actions.touchnet_azure_ad_client_id_test}")
private String clientIdTest;
#Value("${actions.touchnet_azure_ad_client_id_prod}")
private String clientIdProd;
#Value("${touchnet.redirectURLDev}")
private String redirectURLDev;
#Value("${touchnet.redirectURLTest}")
private String redirectURLTest;
#Value("${touchnet.redirectURLProd}")
private String redirectURLProd;
private String clientId;
private String clientSecret;
private String redirectURI;
public SSOUtil() {
this.redirectURI = redirectURLTest;
this.clientSecret = clientSecretTouchnetTest;
}
public String getADLoginURL() {
String returnURL = "";
System.out.println(clientIdTest); // always prints null
}
}
The values in this object work just fine, though, and note that one of them is the same #Value as in the other class:
#Service
public class LibraryHelpServiceBean implements LibraryHelpService {
private CourseServiceBean courseServiceBean;
private final RestTemplate restTemplate;
#Value("${actions.libraryhelp_lti_api_key}")
private String apikey;
#Value("${actions.touchnet_azure_ad_client_id_test}")
String clientIdTest;
public LibraryHelpServiceBean(CourseServiceBean courseServiceBean, RestTemplateBuilder restTemplateBuilder) {
this.courseServiceBean = courseServiceBean;
this.restTemplate = restTemplateBuilder.build();
}
public void doesValueWork() {
this.apikey = this.apikey;
System.out.println(this.clientIdTest); // always prints correct value, a guid
}
}
Both objects are initialized in a similar way: either directly or indirectly through the #Autowired annotation in other objects that I use (and which work fine, and have worked fine for ages). Here's the creation of SSOUtil (my problem class):
#RestController
#RequestMapping(value = "/web")
public class SSOLandingController {
#Autowired
private SSOUtil ssoUtil;
[rest of class omitted]
}
And here's the creation of LibraryHelpServiceBean, which is working fine and has all #Values populate correctly:
#Service
public class LibraryHelpStreamServiceBean implements LibraryHelpStreamService {
private LibraryHelpServiceBean libraryHelpServiceBean;
public LibraryHelpStreamServiceBean(LibraryHelpServiceBean libraryHelpServiceBean){
this.libraryHelpServiceBean = libraryHelpServiceBean;
}
}
I have already tried changing the class annotation for SSOUtil from #Service to #Component (and #Configuration, just for the heck of it).
What could be causing the #Values in SSOUtil to come back null even though some of those same #Values populate just fine in other classes?
I'm convinced that I'm missing something obvious. I'm hoping it's something small like a typo. I'm nervous that it's something big, like I've completely misunderstood how Spring IOC works for the past several years.
Thanks for your help.
I tested your case on my computer, but I'm not able to reproduce your problem. When things like this is happening, try something very simple like this
package no.mycompany.springbootapp;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class SSOUtil2 {
#Value("${actions.touchnet_azure_ad_client_id_test}")
private String clientIdTest;
}
Inject this component into your controller, set a breakpoint inside your controller method and inspect the injected instance.
My experience is that some unexplainable cases I've been involved in here on SO, were solved by cleaning the build or wiping the .m2-folder.

Groovy + Spring - DI with no boilerplate (constructor) code

I have created spring-boot project which bases on Groovy instead of Java.
Now I have following #RestController:
#RestController
class HelloRest {
private final HelloService helloService
#GetMapping("hello")
String hello(#RequestParam("name") String name) {
helloService.createHelloMessage(name)
}
}
Question is how to inject
#Service
class HelloService {...}
in most simple way avoiding boilerplate (in this case the constructor) code?
In Java I would use: #lombok.RequiredArgsConstructor and in fact it works also if I use it in my groovy project.
On the other hand for example the #Immutable annotation from groovy.transform doesn't work as it creates in fact more than single constructor. Whereas Spring expects single constructor to be able automatically #Autowired the dependencies.
As far I see 2 solutions:
Generate the constructor
Use lombok with its annotations
Is there any solution build into Groovy which could be used here instead?
At this moment there is no Groovy mechanism that does same thing as #lombok.RequiredArgsConstructor. The main problem in your case is that Groovy always generates no-args default constructor for all currently known features like #Immutable annotation. The closest (but not accurate) way is to use #TupleConstructor like:
#RestController
#TupleConstructor(includes = ['helloService'], includeFields = true, includeProperties = false, force = true)
class HelloRest {
private final HelloService helloService
#GetMapping("hello")
String hello(#RequestParam("name") String name) {
return helloService.createHelloMessage(name)
}
}
This Groovy code will produce bytecode similar to this Java code:
#RestController
#TupleConstructor(
includeFields = true,
force = true,
includeProperties = false,
includes = {"helloService"}
)
public class HelloRest implements GroovyObject {
private final HelloService helloService;
public HelloRest(HelloService helloService) {
CallSite[] var2 = $getCallSiteArray();
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
this.helloService = (HelloService)ScriptBytecodeAdapter.castToType(helloService, HelloService.class);
}
public HelloRest() {
CallSite[] var1 = $getCallSiteArray();
this((HelloService)null);
}
#GetMapping({"hello"})
public String hello(#RequestParam("name") String name) {
CallSite[] var2 = $getCallSiteArray();
return (String)ShortTypeHandling.castToString(var2[0].call(this.helloService, name));
}
}
It is almost what you need, except this default constructor that was generated as well.
Things are getting even more complicated when using #Immutable annotation, because this version would generate 3 constructors:
public HelloRest(HelloService helloService)
public HelloRest()
public HelloRest(HashMap args)
Of course in this case you would have to remove private final in front of HelloService field definition, because this AST transformation works only with fields that are not yet final.
In this case two options you have found (creating construct manually or using Lombok) are probably the best solutions to your problem.
Alternative solution
There is also one "dirty" solution that allows you to write less amount of code, but promotes injection by reflection. Consider following code:
#RestController
class HelloRest {
#Autowired
private final HelloService helloService
#GetMapping("hello")
String hello(#RequestParam("name") String name) {
return helloService.createHelloMessage(name)
}
}
It will generate bytecode similar to following Java code:
#RestController
public class HelloRest implements GroovyObject {
#Autowired
private final HelloService helloService;
public HelloRest() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
#GetMapping({"hello"})
public String hello(#RequestParam("name") String name) {
CallSite[] var2 = $getCallSiteArray();
return (String)ShortTypeHandling.castToString(var2[0].call(this.helloService, name));
}
}
Even though there is only single default constructor that does not even touch our helloService field, Spring bean will get injected by reflection. I share this option only to show all alternatives, although your initial instinct to use constructor injection is the best possible way to use dependency injection in practice.
You can use #Cannonical or #Immutable on the class. That way the constructor will be created for you

How do I mock an autowired #Value field in Spring with Mockito?

I'm using Spring 3.1.4.RELEASE and Mockito 1.9.5. In my Spring class I have:
#Value("#{myProps['default.url']}")
private String defaultUrl;
#Value("#{myProps['default.password']}")
private String defaultrPassword;
// ...
From my JUnit test, which I currently have set up like so:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
I would like to mock a value for my "defaultUrl" field. Note that I don't want to mock values for the other fields — I'd like to keep those as they are, only the "defaultUrl" field. Also note that I have no explicit "setter" methods (e.g. setDefaultUrl) in my class and I don't want to create any just for the purposes of testing.
Given this, how can I mock a value for that one field?
You can use the magic of Spring's ReflectionTestUtils.setField in order to avoid making any modifications whatsoever to your code.
The comment from Michał Stochmal provides an example:
use ReflectionTestUtils.setField(bean, "fieldName", "value"); before invoking your bean method during test.
Check out this tutorial for even more information, although you probably won't need it since the method is very easy to use
UPDATE
Since the introduction of Spring 4.2.RC1 it is now possible to set a static field without having to supply an instance of the class. See this part of the documentation and this commit.
It was now the third time I googled myself to this SO post as I always forget how to mock an #Value field. Though the accepted answer is correct, I always need some time to get the "setField" call right, so at least for myself I paste an example snippet here:
Production class:
#Value("#{myProps[‘some.default.url']}")
private String defaultUrl;
Test class:
import org.springframework.test.util.ReflectionTestUtils;
ReflectionTestUtils.setField(instanceUnderTest, "defaultUrl", "http://foo");
// Note: Don't use MyClassUnderTest.class, use the instance you are testing itself
// Note: Don't use the referenced string "#{myProps[‘some.default.url']}",
// but simply the FIELDs name ("defaultUrl")
You can use this magic Spring Test annotation :
#TestPropertySource(properties = { "my.spring.property=20" })
see
org.springframework.test.context.TestPropertySource
For example, this is the test class :
#ContextConfiguration(classes = { MyTestClass.Config.class })
#TestPropertySource(properties = { "my.spring.property=20" })
public class MyTestClass {
public static class Config {
#Bean
MyClass getMyClass() {
return new MyClass ();
}
}
#Resource
private MyClass myClass ;
#Test
public void myTest() {
...
And this is the class with the property :
#Component
public class MyClass {
#Value("${my.spring.property}")
private int mySpringProperty;
...
I'd like to suggest a related solution, which is to pass the #Value-annotated fields as parameters to the constructor, instead of using the ReflectionTestUtils class.
Instead of this:
public class Foo {
#Value("${foo}")
private String foo;
}
and
public class FooTest {
#InjectMocks
private Foo foo;
#Before
public void setUp() {
ReflectionTestUtils.setField(Foo.class, "foo", "foo");
}
#Test
public void testFoo() {
// stuff
}
}
Do this:
public class Foo {
private String foo;
public Foo(#Value("${foo}") String foo) {
this.foo = foo;
}
}
and
public class FooTest {
private Foo foo;
#Before
public void setUp() {
foo = new Foo("foo");
}
#Test
public void testFoo() {
// stuff
}
}
Benefits of this approach: 1) we can instantiate the Foo class without a dependency container (it's just a constructor), and 2) we're not coupling our test to our implementation details (reflection ties us to the field name using a string, which could cause a problem if we change the field name).
You can also mock your property configuration into your test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
#Configuration
public static class MockConfig{
#Bean
public Properties myProps(){
Properties properties = new Properties();
properties.setProperty("default.url", "myUrl");
properties.setProperty("property.value2", "value2");
return properties;
}
}
#Value("#{myProps['default.url']}")
private String defaultUrl;
#Test
public void testValue(){
Assert.assertEquals("myUrl", defaultUrl);
}
}
I used the below code and it worked for me:
#InjectMocks
private ClassABC classABC;
#Before
public void setUp() {
ReflectionTestUtils.setField(classABC, "constantFromConfigFile", 3);
}
Reference: https://www.jeejava.com/mock-an-autowired-value-field-in-spring-with-junit-mockito/
Also note that I have no explicit "setter" methods (e.g. setDefaultUrl) in my class and I don't want to create any just for the purposes of testing.
One way to resolve this is change your class to use Constructor Injection, that can be used for testing and Spring injection. No more reflection :)
So, you can pass any String using the constructor:
class MySpringClass {
private final String defaultUrl;
private final String defaultrPassword;
public MySpringClass (
#Value("#{myProps['default.url']}") String defaultUrl,
#Value("#{myProps['default.password']}") String defaultrPassword) {
this.defaultUrl = defaultUrl;
this.defaultrPassword= defaultrPassword;
}
}
And in your test, just use it:
MySpringClass MySpringClass = new MySpringClass("anyUrl", "anyPassword");
Whenever possible, I set the field visibility as package-protected so it can be accessed from the test class. I document that using Guava's #VisibleForTesting annotation (in case the next guy wonders why it's not private). This way I don't have to rely on the string name of the field and everything stays type-safe.
I know it goes against standard encapsulation practices we were taught in school. But as soon as there is some agreement in the team to go this way, I found it the most pragmatic solution.
Another way is to use #SpringBootTest annotation properties field.
Here we override example.firstProperty property:
#SpringBootTest(properties = { "example.firstProperty=annotation" })
public class SpringBootPropertySourceResolverIntegrationTest {
#Autowired private PropertySourceResolver propertySourceResolver;
#Test
public void shouldSpringBootTestAnnotation_overridePropertyValues() {
String firstProperty = propertySourceResolver.getFirstProperty();
String secondProperty = propertySourceResolver.getSecondProperty();
Assert.assertEquals("annotation", firstProperty);
Assert.assertEquals("defaultSecond", secondProperty);
}
}
As you can see It overrides only one property. Properties not mentioned in #SpringBootTest stay untouched. Therefore, this is a great solution when we need to override only specific properties for the test.
For single property you can write it without braces:
#SpringBootTest(properties = "example.firstProperty=annotation")
Answer from: https://www.baeldung.com/spring-tests-override-properties#springBootTest
I also encourage you to whenever possible pass property as a parameter in constructor like in Dherik answer (https://stackoverflow.com/a/52955459/1673775) as it enables you to mock properties easily in unit tests.
However in integration tests you often don't create objects manually, but:
you use #Autowired
you want to modify property used in a class that is used in your integration test indirectly as it is deep dependency of some directly used class.
then this solution with #SpringBootTest might be helpful.

Resources