Unable to load external properties in Spring Boot - spring

I have a very basic spring boot command line app into which I am trying to load properties from an application.yml file that is present inside my project. The project is built using gradle and I am using groovy as the language.
Now wherever I place the application.yml file, I can't seem to be able to assign its values to the configuration properties bean. The files are as below
application.yml
my:
name: "some name"
servers:
- dev.bar.com
- foo.bar.com
Config.groovy
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration
#Configuration
#ConfigurationProperties(prefix = "my")
public class Config {
private List<String> servers = new ArrayList<String>();
private String name
public List<String> getServers() {
return this.servers;
}
public String getName() {
return this.name
}
}
Worker.groovy The Command line runner
#Component
class Worker implements CommandLineRunner {
#Autowired
Config config
#Override
public void run(String... args) throws Exception {
println "Running a test app"
println config.name
println config.getServers()
}
}
Directory Structure
app-name
-build.gradle
-src
-main
-groovy
-config
-application.yml
-com
-company
-app
Worker.groovy
Config.groovy
Application.groovy
I also tried renaming application.yml to application.properties as well as adding it under the com.company.app project, but the worker class's run method always prints the property as null. I believe I may have missed something very basic, but can't seem to find what it has.
Let me know if I need to provide any additional details.

application.yml should go under src/main/resources/config.
Resources are processed (copied) from src/main/resources directory.
By default the groovy plugin ignores other than groovy classes in src/main/groovy.

Related

Getting properties in under test Service class by #Value returns null

I have a Spring Boot version 2.7.0 project with different profiles for dev and test with two different properties files: application-dev.properties and application-test.properties. (I have NO default application.properties file.)
In under test service class I have a property that I want to load its value from application-test.properties file. The service class:
#Service
#RequiredArgsConstructor
public class FileServiceImpl implements FileService {
#Value("${files.root-directory}")
private String fileDirectory;
#Override
#Transactional(readOnly = false)
public File createFile(CreateFileCommand command) {
var filePath = FileUtil.getPath(fileDirectory); // <- fileDirectory is null in tests
// ....
}
}
When I run the application in dev profile, everything is OK. But in tests, fileDirectory is always null.
Test class:
#SpringBootTest
#ActiveProfiles("test")
#ExtendWith(MockitoExtension.class)
public class FileServiceTest {
// ...
}
ApplicationTest class:
#SpringBootTest
#ActiveProfiles("test")
#TestPropertySource("classpath:application-test.properties")
#EnableConfigurationProperties
class ApiApplicationTests {
#Test
void contextLoads() {
}
}
application-dev.properties file:
files.root-directory=${user.home}\\api\\files
application-dev.properties file:
files.root-directory=/home/api/var/api/files
EDIT Screenshot of file structure
Put your properties file under src/test/resources for your (JUnit,Integration, Contract etc) test files. That should work!
Property files under src/main/resources are accessible to source files present under src/main/ only!

Spring boot #ConfigurationProperties not working

I'm using #ConfigurationProperties for auto configuration of properties. My code is working in IDE. But when I run the jar in command line, it is not working.
Configuration class:
#Configuration
#ConfigurationProperties(prefix="location")
public class Location {
private String base;
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
}
Main class:
#SpringBootApplication
#EnableConfigurationProperties(Location.class)
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
application.yml:
location:
base: c:\test
If I autowire Location class, I see an instance created. But there is not property set. The code is not entering setBase() method.
The application prints this in the console.
AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject'
annotation found and supported for autowiring
Make sure that application.yml file is in the root of your classpath, usually it's put in the resources folder.
The fact that the setBase() method is not called suggests that your application.yml file is not being found. Spring looks in the root of your classpath for the application.yml file.
The comment from M. Deinum is correct saying that your duplicated annotations will result in 2 spring beans for Location class. However, as you say you managed to autowire the bean without getting an error it suggests that your Location class isn't in a package that is found by spring when it's scanning for beans. If there were 2 beans then you'd get an error when you autowired it. By default spring will scan use the package where the #SpringBootApplication is as the base. It will then look in this package and all sub packages.
If your package structure is like this...
myapp.main
Application.java
myapp.config
Location.java
Then you need to add scanBasePackages="myapp" to the #SpringBootApplication annotation.
Also change your main class and remove the #Enable.. annotations. i.e.:
#SpringBootApplication(scanBasePackages="myapp")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
As nothing worked with yaml, I had to change to property file and use
#PropertySource({"classpath:application.properties"})
for the spring to identify the properties file

spring boot application context was not properly loaded if the yaml file wasn't application.yml

With following configuration, my test can read the properties from the yaml file correctly.
#SpringBootApplication
#PropertySource("classpath:application.yml")
#ComponentScan({ "com.my.service" })
public class MyApplication {
}
Then I renamed the yaml file to my-application.yml, and changed the PropertySource to
#PropertySource("classpath:my-application.yml")
Tests are failed due to the null property value. The configuration class is as following:
#Configuration
#ConfigurationProperties(prefix="my")
#Data
public class MyConfig {
private String attr1;
}
The test class is:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
public class MyConfigTest {
#Autowired
private MyConfig myConfig;
#Test
public void getMyConfigTest() {
Assert.assertNotNull(myConfig.getAttr1());
}
Why spring boot can find the renamed yaml file, but it couldn't load the value correctly?
YAML files can’t be loaded via the #PropertySource annotation
It appears to work with #PropertySource("classpath:application.yml") because that's the default location and spring boot looks there regardless.
You may be able to use #ConfigurationProperties(location="claspath:my-application.yml") instead but it doesn't really achieve the same purpose (and I've never tried it myself).

Spring Boot WS application cannot load external property

In my Spring-Boot Web Service application, I want to load a property named appName with value defined in application.properties.
#Endpoint
public class RasEndpoint {
private static final String NAMESPACE_URI = "http://www.mycompany.com/schema/ras/ras-request/V1";
#Value("${appName}")
private String appName;
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getProductRequest")
#ResponsePayload
public GetProductResponse getProduct(#RequestPayload GetProductRequest request) {
System.out.println("appName: " + appName);
GetProductResponse response = generateStubbedOkResponse();
return response;
}
application.properties has the following entry
appName=ras-otc
I get the application started via the main Application class as shown below
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
However, when I run the app, I get the following error
Caused by: java.lang.IllegalArgumentException: Could not resolve
placeholder 'appName' in string value "${appName}"
Do you guys know what I'm doing wrong?
Appreciate any help.
As Dave mentioned in the comment above, the properties file was not loaded into the classpath.
The properties file was located in /src/main/resources folder, which was added to source, under build path in Eclipse IDE, however had an exclusion rule applied which prevented the properties file from being loaded into the classpath. By removing the exclusion, I was able to load the properties correctly.

How to externalize application.properties to an external filesystem location in Spring Boot?

#ComponentScan
#EnableAutoConfiguration
#PropertySource(value = { "file:/Users/Documents/workspace/application.properties" })
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
}
In this case it gives while deploying:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.jdbc.core.JdbcTemplate] found for dependency:
Not able to find the correct way to externalize the application properties file
I tried autowiring environment variable which is correctly loaded but then I need to manually define all the beans
#Bean
public JdbcTemplate dataSource() {
String driverClassName = env
.getProperty("spring.datasource.driverClassName");
String dsUrl = env.getProperty("spring.datasource.url");
String username = env.getProperty("spring.datasource.username");
String password = env.getProperty("spring.datasource.password");
//DataSource dataSource = new SimpleDriverDataSource(new driverClassName, dsUrl, username, password);
JdbcTemplate jdbc = new JdbcTemplate(dataSource);
return jdbc;
}
This deploys without throwing error but not responding.
If you're deploying a WAR to Tomcat, then you need to define a context XML as described here: https://tomcat.apache.org/tomcat-7.0-doc/config/context.html#Defining_a_context
For example, you would typically define $CATALINA_BASE/conf/Catalina/localhost/my-app.xml if you have http://localhost:8080/my-app/.
The file would then look like this:
<Context docBase='path/to/my-app/my-app.war'>
<Environment name='app_config_dir' value='path/to/my-app/' type='java.lang.String'/>
</Context>
In your code, implement ApplicationContextAware and override setApplicationContext. Here is an example:
public class Setup implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("Set application context. App Config Property: {}",applicationContext.getEnvironment().getProperty("app_config_dir"));
}
}
You can now load the properties file from app_config_dir.
There is no need to define it by using #PropertySource annotation. You should just use spring.config.location property to set your application.properties location. That property can be set up for example in command line:
java -jar myapp.jar --spring.config.location=/Users/Documents/workspace/
You can define an environment variable called SPRING_CONFIG_LOCATION and give it a value that can be a folder (which should end in /) or a file. In any case the location should pe prefixed with file::
SPRING_CONFIG_LOCATION = file:C:/workspace/application.properties
or
SPRING_CONFIG_LOCATION = file:C:/workspace/
More about this here.
You can use #PropertySource too because it is useful when you are deploying application as war file to tomcat.
#PropertySource is not working because you are missing #Configuration annotation. As per the Spring-Boot documentation, #PropertySource annotations should be present on your #Configuration classes.
Following works
#Configuration
#ComponentScan
#EnableAutoConfiguration
#PropertySource(value = { "file:/Users/Documents/workspace/application.properties" })
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
}
You can also load multiple properties file. For ex:
#PropertySource(value = {"classpath:application.properties", "file:/Users/overriding.propertis"}, ignoreResourceNotFound = true)
Note that the order of the declared files is important. If the same key is defined in two or more files, the value associated with the key in the last declared file will override any previous value(s).

Resources