Failed to configure a DataSource from command line - spring-boot

I'm trying to change Datasource setting from command Line. I have a application.properties with default setting. I would like to modify the parameters in the file from command Line, but when I pass Datasource arguments , I receive an error. I read on Externalized Configuration document :"Accessing Command Line Properties
By default, SpringApplication converts any command line option arguments (that is, arguments starting with --, such as --server.port=9000) to a property and adds them to the Spring Environment. As mentioned previously, command line properties always take precedence over other property sources".
I supposed that arguments overwrite the default setting into application.properties, but I'm missng some steps about that.
I've tried without spring.datasource.url or Placeholders in Properties. Below application.properties.
spring.datasource.url = jdbc:oracle:thin:#servername:port:DB11G
#spring.datasource.url = ${spring.datasource.url}
spring.datasource.driver.class=oracle.jdbc.driver.OracleDriver
spring.datasource.username = dbUser
spring.datasource.password = password
My application with datasource default settings run well.
this is my code:
spring.datasource.url = ${db.url}
spring.datasource.driver.class=oracle.jdbc.driver.OracleDriver
spring.datasource.username = dbUser
spring.datasource.password = dbPassword
Main class
#SpringBootConfiguration
public class IdsFeApplication implements ApplicationRunner{
private static final String FEC_CODEX = "A";
#Autowired
private static ConfigInfoDB infoDb;
#Autowired
private Login fec;
public static void main(String[] args) throws InterruptedException {
SpringApplication bootApp = new SpringApplication(IdsFeApplication.class);
bootApp.setBannerMode(Banner.Mode.OFF);
bootApp.setLogStartupInfo(false);
ConfigurableApplicationContext context = bootApp.run(args);
ConfigInfoDB db=context.getBean(ConfigInfoDB.class);
db.dbInfo();
}
#Override
public void run(ApplicationArguments args) throws Exception {
// TODO Auto-generated method stub
fec.token(FEC_CODEX);
}
}

Change your top annotation from #SpringBootConfiguration to #SpringBootApplication.
#SpringBootApplication in reality is a shortcut for #configuration, #EnableAutoConfiguration and #ComponentScan.
https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-using-springbootapplication-annotation.html
#EnableAutoConfiguration does a lot of the magic behind the scenes of configuring your application based on what dependencies are included and what information it finds in the environment.

Related

Spring Boot - Nested Configuration Properties are not recognized

As the title says, my custom properties are not registered when Application is starting. Would you mind taking a look?
My custom.yml:
bridge:
url: xxx
authentication:
auth-url: xxx
user: xxx
My BridgeProperties.java:
#Component
#PropertySource("classpath:custom.yml")
#ConfigurationProperties(
prefix = "bridge"
)
public class BridgeProperties {
#NestedConfigurationProperty
protected ApiProperties apiProperties = new ApiProperties();
}
My ApiProperties.java:
public class ApiProperties {
protected String url;
protected ApiProperties.Authentication authentication;
// getter and setter
public ApiProperties() {
}
public static class Authentication {
protected String authUrl;
protected String user;
public Authentication() {}
// getter and setter
}
My Application's entry point:
#SpringBootApplication
#PropertySources(value = {
#PropertySource("classpath:application.yml"),
#PropertySource("classpath:custom.yml")
})
public class IntegrationService {
public static void main(String... args) {
SpringApplication.run(IntegrationService.class, args);
}
}
When printing to the command line, I get null instead of the values I assigned to url, auth-url, and user in the custom.yml.
As mentioned in the Baeldung Site:
It's also worth mentioning that YAML files do not support the #PropertySource annotation, so if we need to use this annotation, it would constrain us to using a properties file.
If you really need to import additional external file in yaml format, you have to implement your own PropertySourceFactory like in this article.
For me I do not see the advantage not using the default application.yml as you can define multiple sections managed by different Properties classes.
For example, if you just put your properties in the application.yml and if you compile with a recent JDK (>=jdk 14) you can even use the record for managing your properties in a very compact manner:
What you have to do is just to add the EnableConfigurationProperties annotation:
#SpringBootApplication
#EnableConfigurationProperties(BridgeProperties.class)
public class IntegrationService {
public static void main(String... args) {
SpringApplication.run(IntegrationService.class, args);
}
}
and define a record for mapping your yaml properties:
#ConfigurationProperties(prefix = "bridge")
public final record BridgeProperties(String url, Authentication authentication) {
public record Authentication(String authUrl, String user){
}
}
Remember you can also define environment variable in profiles like application-dev.yml if you start your app with profile dev and another one e.g. for production application-prd.yml if your profile for production is named prd. You can still keep the common properties in the application.yml.
If you really want to go in the external file path, I suggest to switch to .properties as the #PropertySource supports it natively.

Is there a possibility to group properties using Spring Boot?

Can something like this be accomplished using Spring Boot?
The idea is to group properties and assign the same value to all of them, so instead of all of the properties ending with 'test*' i would like to change just one property 'my.flag'. I know that such functionality works in case of loggers, but can I define my own group?
I am not sure whether your problem has been solved or not, but I want to provide a solution to achieve what you want by using spring.factories and implementing ApplicationListener as following steps.
STEP 1
Create a class MyPropertiesListener which implements ApplicationListener and read the value of my.flag in application.properties first, then set it to all the properties whose key starts with my.flag..
public class MyPropertiesListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
#Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment env = event.getEnvironment();
String myFlag = env.getProperty("my.flag");
Properties props = new Properties();
MutablePropertySources propSrcs = env.getPropertySources();
StreamSupport.stream(propSrcs.spliterator(), false)
.filter(ps -> ps instanceof EnumerablePropertySource)
.map(ps -> ((MapPropertySource) ps).getPropertyNames())
.flatMap(Arrays::<String>stream)
.forEach(propName -> {
if (propName.toString().startsWith("my.flag.")) {
props.put(propName, myFlag);
}
});
env.getPropertySources().addFirst(new PropertiesPropertySource("myProps", props));
}
}
STEP 2
Create a file named spring.factories under src/main/resource/META-INF and configure MyPropertiesListener into it.
org.springframework.context.ApplicationListener=xxx.xxx.xxx.MyPropertiesListener
TEST
The value of my.flag.test3 is false in application.properties originally, but it is going to be overwritten as true while application starts.
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Value("${my.flag.test3}")
private String test3;
#Override
public void run(String... args) throws Exception {
System.out.println(test3); //true
}
}
See also
Creating a Custom Starter with Spring Boot

Spring #ConfigurationProperties not populated

I am experiencing problems using the #ConfigurationProperties feature.
Probably, I am missing something, since the mechanism seems very simple, but for me, it does not work.
I am using Spring Boot with the following main Application class
#SpringBootApplication
#EnableAspectJAutoProxy
#EnableConfigurationProperties(QueuesProperties.class)
#PropertySource("file:config/queues.properties")
#ImportResource("classpath:/spring-config.xml")
public class Application {
public static void main(String... args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
with QueuesProperties
#ConfigurationProperties(prefix = "wmq.in.queue")
public class QueuesProperties {
private static final Logger LOGGER = LoggerFactory.getLogger(QueuesProperties.class);
private String descr;
public String getDescr() {
return descr;
}
public void setDescr(String descr) {
this.descr = descr;
}
}
The properties file is very simple (I am trying to isolate the problem)
wmq.in.queue.descr = description
Then, I am trying to #Autowired the QueuesProperties in a #Component that I use in a spring-integration flow with a .
The QueuesProperties is correctly injected but the descr attribute is null.
#Autowired
private QueuesProperties queuesConfiguration;
while this
#Value("${wmq.in.queue.descr}")
private String descr;
is correctly evaluated.
I have made a lot of attempt with different configurations or code, but the result is the same. I get the QueuesProperties bean but it is not populated.
What am I missing?
Reading the question isn't very clear if the wmq.in.queue.descr = description properties is written in applciation.properties file. I said it because you say that the properties is correctly evaluated with #Value and not with
#Autowired
private QueuesProperties queuesConfiguration;
Even the #PropertySource("file:config/queues.properties") let me to think that probably the your wmq.in.queue.descr = description properties isn't written in applciation.properties but in file:config/queues.properties.
Summing
For use #ConfigurationProperties feature you have write the properties in application.properties and use #EnableConfigurationProperties(QueuesProperties.class) on #Component, #Configuration and so on annotated classes like below.
#Component
#EnableConfigurationProperties(QueuesProperties.class)
public class YourBean {
....
private final QueuesProperties queuesProperties;
public YourBean(QueuesProperties queuesProperties){
this.queuesProperties = queuesProperties;
}
.....
}
actually you can change the application.properties file name customizing spring boot properties evaluation but for your local app I discourage. I consider application.properties a good name for naming a place in which you put the configuration properties of your application
I hope that it can help you

Unable to load external properties in Spring Boot

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.

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