Spring Boot not able to pick value from yml file - spring-boot

I have a application-local.yml file inside resources folder. I have some properties like
data: https://xyzw/
I am using this property in a different class
#Service
public class Test {
#Value("${data}")
private String data;
}
When I run the Application.java I get the following error:
Application.main(Application.java:13), exception_class=org.springframework.beans.factory.BeanCreationException, exception_message=Error creating bean with name 'Test': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'data' in value "${data}"}","threadID":"main","sourceHost":"H18NPLFI13P0303","logVersion":"1.5","category":"org.springframework.boot.SpringApplication"}
Which is weird. I am not sure why it's not picking the values from the application-local.yml file.

If you start your application with IntelliJ IDEA and want the application-local.yml properties to be used, you need to edit your run configuration so that the local profile is used, else the application-local.yml properties won't be picked up by Spring.

Related

How to use #Value in Junit tests

I'm trying to get a property value from my application-test.yml file like that :
#SpringBootTest()
#ActiveProfiles("test")
#ExtendWith(MockitoExtension.class)
public class GoogleServiceUtilsTest {
#Value("${google.service.account.user}")
private String serviceAccountUser;
#Value("${google.service.account.path}")
private String pathFile;
Set<String> scopesSet = DirectoryScopes.all();
List<String> scopesList = new ArrayList<String>(scopesSet);
#Test
void getCredentialTest() throws GoogleCredentialException {
// Given
GoogleCredentials credentials;
// When
credentials = GoogleServiceUtils.getCredential(serviceAccountUser, scopesList, pathFile);
// Then
assertThat(credentials != null);
assertThat(new HttpCredentialsAdapter(credentials));
}
}
but when I use it in my test method, serviceAccountUser and pathFile variables are always null.
My application-test.yml file is located in 'src/test/resources', and tests are in 'src/test/java/' and content :
google:
service:
account:
user: ...
path: ...
# FED credentials
fed:
url: ...
token: ...
grantType: ...
# Logging
logging:
level:
root: ...
org.springframework: ...
When I use the #Value tag in my app code, all is working. Variables are getting good values from 'src/main/resources/application.yml' file.
After reading comments, I add that the profiles are not useful in my case, but as when I don't use them, it doesn't work, I thought that maybe it came from there and that it is necessary to use them.
I also tried to write the value of the variables hard in the file, and then there is no problem, tests are passing well.
Is someone understanding this problem? Many people seem to have had the same, but I can't find an answer working for me.
Thank you for your answer!
Sorry, cannot reproduce!
Having:
Simplest starter
src/main/resources/application.yml
foo:
bar:
baz: normal
...and
src/test/resources/application-test.yml
foo:
bar:
baz: test
This test passes:
#SpringBootTest
#ActiveProfiles("test") // !
class SomeTest {
#Value("${foo.bar.baz}")
String foo;
#Test
void testProp() {
Assertions.assertEquals("test", foo); //!#
}
}
Either #ExtendWith(MockitoExtension.class) has no effect/does no harm.
Whenever you wonder, where your properties come from
Consult:https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config
And check (current version):
...considered in the following order:
Default properties (specified by setting SpringApplication.setDefaultProperties).
#PropertySource annotations on your #Configuration classes. Please note ...
Config data (such as application.properties files).
A RandomValuePropertySource that has properties only in random.*.
OS environment variables.
Java System properties (System.getProperties()).
JNDI attributes from java:comp/env.
ServletContext init parameters.
ServletConfig init parameters.
Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
Command line arguments.
properties attribute on your tests. Available on #SpringBootTest and the test annotations for testing a particular slice of your application.
#TestPropertySource annotations on your tests.
Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active.
Config data files (3.) are considered in the following order:
Application properties packaged inside your jar (application.properties and YAML variants).
Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants).
Application properties outside of your packaged jar (application.properties and YAML variants).
Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants).

Running 'mvn clean install' on Spring Boot application using env variables in application.properties

Hello I am trying to package my Spring Boot app into a jar.
I want to deploy this app to AWS Beanstalk and so I will be injecting some variables into application.properties using Environment variables.
spring.data.mongodb.uri=${MONGODB_URI}
spring.data.mongodb.auto-index-creation=true
spring.servlet.multipart.max-file-size=-1
spring.servlet.multipart.max-request-size=-1
CLOUDINARY_URL=${CLOUDINARY_URL}
jwt-secret=${JWT_SECRET}
server.port=5000
However when I run the maven command (mvn clean install), during the package process the code is executed and it is failing stating that
BeanCreationException: Error creating bean with name 'customBeansConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'CLOUDINARY_URL' in value "${CLOUDINARY_URL}"
I have a class CustomBeansConfig:
#Configuration
public class CustomBeansConfig {
#Value("${CLOUDINARY_URL}")
private String cloudinaryUrl;
#Bean
public Cloudinary cloudinary(){
System.out.println("cloudinaryUrl = " + cloudinaryUrl);
return new Cloudinary(cloudinaryUrl);
}
}
Please help me to create the jar file
If I have understood you correctly, one approach may be to use different application.properties files for different environments. For example application-dev.properties for the Dev environment and application-prod.properties for the Prod environment. Then your CLOUDINARY_URL may be assigned different literal values appropriate to each.
Then when deploying to each environment, bundle your JAR with the -Denv option, as in
mvn -Denv=dev clean install
OR
mvn -Denv=prod clean install
... and upload the resulting JAR file to the corresponding AWS environment.
Running the Spring Boot application with a such config property, got me the following error:
Caused by: java.lang.IllegalArgumentException: Circular placeholder reference 'CLOUDINARY_URL' in property definitions
Changing the name of your Spring property from CLOUDINARY_URL to, for example, cloudinary.service.url will resolve the issue.
In such case, your config file should look like this:
spring.data.mongodb.uri=${MONGODB_URI}
spring.data.mongodb.auto-index-creation=true
spring.servlet.multipart.max-file-size=-1
spring.servlet.multipart.max-request-size=-1
cloudinary.service.url=${CLOUDINARY_URL}
jwt-secret=${JWT_SECRET}
server.port=5000
And your configuration file like this:
#Configuration
public class CustomBeansConfig {
#Value("${cloudinary.service.url}")
private String cloudinaryUrl;
#Bean
public Cloudinary cloudinary(){
System.out.println("cloudinaryUrl = " + cloudinaryUrl);
return new Cloudinary(cloudinaryUrl);
}
}
Also, I would advise you to avoid creating Spring configuration properties using the underscore format, since it usually used for the environment variables, maybe be confusing and may cause such interesting issues.

#ConditionalOnProperty not working when building jar locally although it is working fine when getting included from remote repo

To be more specific in the below code if I am providing only the property of MongoDB(spring.data.mongodb) in application.yml it is throwing RunTime exception stating that
PostgresDBPurgeController bean can not be created missing spring.datasource
but as per #ConditionalOnProperty if the property is not there Bean creation would not happen.
In detail,I have the below code in a common jar and I have 2 different app(1 use postgres as db ,another mongo as db ) which use this same jar and both the apps are working fine when I am including the jar from remote repo.
But when I build the common jar locally and and run the app (which use mongo as db ) locally it is throwing above error.
#ConditionalOnProperty(prefix = "spring.data.mongodb", name = "host")
#ConditionalOnProperty(prefix = "spring.datasource", name = "driver-class-name", havingValue = "org.postgresql.Driver")
public class PostgresDBPurgeController {

Spring boot on Tomcat with external configuration

I can't find an answer to this question on stackoverflow hence im asking here so I could get some ideas.
I have a Spring Boot application that I have deployed as a war package on Tomcat 8. I followed this guide Create a deployable war file which seems to work just fine.
However the issue I am currently having is being able to externalize the configuration so I can manage the configuration as puppet templates.
In the project what I have is,
src/main/resources
-- config/application.yml
-- config/application.dev.yml
-- config/application.prod.yml
-- logback-spring.yml
So how can I possibly load config/application.dev.yml and config/application.prod.yml externally and still keep config/application.yml ? (contains default properties including spring.application.name)
I have read that the configuration is load in this order,
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The classpath root
Hence I tried to load the configuration files from /opt/apache-tomcat/lib to no avail.
What worked so far
Loading via export CATALINA_OPTS="-Dspring.config.location=/opt/apache-tomcat/lib/application.dev.yml"
however what I would like to know is,
Find out why loading via /opt/apache-tomcat/lib classpath doesn't work.
And is there a better method to achieve this ?
You are correct about load order. According to Spring boot documentation
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The classpath root
The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).
[Note]
You can also use YAML ('.yml') files as an alternative to '.properties'.
This means that if you place your application.yml file to /opt/apache-tomcat/lib or /opt/apache-tomcat/lib/config it will get loaded.
Find out why loading via /opt/apache-tomcat/lib classpath doesn't work.
However, if you place application.dev.yml to that path, it will not be loaded because application.dev.yml is not filename Spring is looking for. If you want Spring to read that file as well, you need to give it as option
--spring.config.name=application.dev or -Dspring.config.name=application.dev.
But I do not suggest this method.
And is there a better method to achieve this ?
Yes. Use Spring profile-specific properties. You can rename your files from application.dev.yml to application-dev.yml, and give -Dspring.profiles.active=dev option. Spring will read both application-dev.yml and application.yml files, and profile specific configuration will overwrite default configuration.
I would suggest adding -Dspring.profiles.active=dev (or prod) to CATALINA_OPTS on each corresponding server/tomcat instance.
I have finally simplified solution for reading custom properties from external location i.e outside of the spring boot project. Please refer to below steps.
Note: This Solution created and executed windows.Few commands and folders naming convention may vary if you are deploying application on other operating system like Linux..etc.
1. Create a folder in suitable drive.
eg: D:/boot-ext-config
2. Create a .properties file in above created folder with relevant property key/values and name it as you wish.I created dev.properties for testing purpose.
eg :D:/boot-ext-config/dev.properties
sample values:
dev.hostname=www.example.com
3. Create a java class in your application as below
------------------------------------------------------
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
#PropertySource("classpath:dev.properties")
#ConfigurationProperties("dev")
public class ConfigProperties {
private String hostname;
//setters and getters
}
--------------------------------------------
4. Add #EnableConfigurationProperties(ConfigProperties.class) to SpringBootApplication as below
--------------------------------------------
#SpringBootApplication
#EnableConfigurationProperties(ConfigProperties.class)
public class RestClientApplication {
public static void main(String[] args) {
SpringApplication.run(RestClientApplication.class, args);
}
}
---------------------------------------------------------
5. In Controller classes we can inject the instance using #Autowired and fetch properties
#Autowired
private ConfigProperties configProperties;
and access properties using getter method
System.out.println("**********hostName******+configProperties.getHostName());
Build your spring boot maven project and run the below command to start application.
-> set SPRING_CONFIG_LOCATION=<path to your properties file>
->java -jar app-name.jar

Loading properties file WebSphere Liberty

I have a simple application that uses Spring to load a properties file from the classpath. When deploying this application to WebSphere Liberty 8.5.5 it results in FileNotFoundException.
nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/myprops.properties]
Here is my spring #Configuration class:
#Configuration
#Profile("dev")
#PropertySource("classpath:/myprops.properties")
public class AppConfigDev extends AppConfig {
...
}
I am wondering where in the Liberty directory structure should my properties file reside?
The use of prefix classpath: in your annotation signifies that the given property file would be picked up from WebSphere's, well, classpath, via a ClassLoader.getResources(...) call.
Ref: http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/resources.html#resources-classpath-wildcards
You will need to create a jar of all your properties files for Websphere Liberty to be able to load them.
Ref:
1. https://developer.ibm.com/answers/questions/13384/websphere-liberty-8-5-setting-java-classpath.html
2. Websphere Liberty 8.5: Setting Java classpath
You can set the name/directory of your files in a file named jvm.options and put it in your ${server.config.dir}/jvm.options
Specific example :
In the file: ${server.config.dir}/jvm.options
Add the following line: -DAPP_ENV=PROD
Access the value with: System.getProperty("APP_ENV"); -> PROD

Resources