I want to achieve is that springboot gets the property values configured in the database to complete the automatic configuration. Just like using application.properties
It is possible to programatically override Spring properties at startup. This would be a good place to retrieve the properties from any database that you whish and set them.
Here is an example (copy/pasted from this answer) . You would have to add getting the configuration from the database
#SpringBootApplication
public class Demo40Application{
public static void main(String[] args){
SpringApplication application = new SpringApplication(Demo40Application.class);
Properties properties = new Properties();
properties.put("server.port", 9999);
application.setDefaultProperties(properties);
application.run(args);
}
}
Related
I am writing a Spring Boot project to Run a Cloud Config Server and 3 Micro-services to run as a Client.
I would want to check availability of all properties in application.properties or yaml file before starting the tomcat server.
We already have a similar one for Database Startup Validation, but here I am trying to achieve the same for Property file availability.
Can someone let me know or suggest possible solutions.
You can simply add whatever you want to add in application.properties / yaml file and then access the property and its value in SpringBoot Application like this.
I think this could help you.
application.properties
property1=true
property2=false
property3=abc
property5=1223243
and in SpringBoot Application
TestApplication.java
#SpringBootApplication
public class TestApplication{
#Value("${property1}")
private boolean property1
#Value("${property2}")
private boolean property2
#Value("${property3}")
private String property3
#Value("${property5}")
private int property5
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
[UPDATE]
If we are agreed to create a Pre-Script that run before tomcat startup then
Using the following code snippet we can parse the application.properties file and create object and then loop through the length of properties file and check the Key Value pairs
FileReader reader=new FileReader("application.properties");
Properties p=new Properties();
p.load(reader);
System.out.println(p.getProperty("user"));
System.out.println(p.getProperty("password"));
I have created one spring boot application, I do not want to keep datasource information in application.properties file as this information will keep on changing. So, All data source information have mentioned in external .txt file. This file is placed outside of project. I am reading this file programmatically. Here is the code,
#SpringBootApplication
public class SpringBootApplicationLuncher {
public static void main(String[] args) throws IOException {
Properties mainProperties = new Properties();
mainProperties.load(new FileInputStream("dbconnection.txt"));
SpringApplication.run(SpringBootApplicationLuncher.class,args);
}
file dbconnection.txt is having below information.
spring.datasource.url=jdbc:mysql://<ip address>:3306/<dbname>
spring.datasource.username=<username>
spring.datasource.password=<password>
When i execute the application, I am getting an error,
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Can anyone please help ?
If you want to pass mainProperties into Spring Boot application, you can use SpringApplicationBuilder for this:
#SpringBootApplication
public class SpringBootApplicationLuncher {
public static void main(String[] args) throws Exception {
Properties mainProperties = new Properties();
mainProperties.load(new FileInputStream("dbconnection.txt"));
new SpringApplicationBuilder()
.sources(SpringBootApplicationLuncher.class)
.properties(mainProperties)
.run(args);
}
}
However Spring Boot already provides a way to externiliaze your configuration, check this documentation for more details
There are so many properties which can be defined in application.properties of spring boot application.
But I want to pass properties to configure ssl to spring from inside the code.
server.ssl.enabled=true
# The format used for the keystore
server.ssl.key-store-type=PKCS12
# The path to the keystore containing the certificate
server.ssl.key-store=keys/keystore.jks
# The password used to generate the certificate
server.ssl.key-store-password=changeit
# The alias mapped to the certificate
server.ssl.key-alias=tomcat
So as these will be spring defined properties to be used from application.properties. I just want to set them from the code based on some logic.
For non spring application, I still have some idea that they can be passed as application, session or context properties but I am not aware on how this works in spring.
Any help would be appreciated.
Since you know the properties to enable SSL in the spring boot app. You can pass these properties programmatically in your spring boot application like this:
#SpringBootApplication
public class SpringBootTestApplication {
public static void main(String[] args) {
// SpringApplication.run(SpringBootTestApplication.class, args);
Properties props = new Properties();
props.put("server.ssl.key-store", "/home/ajit-soman/Desktop/test/keystore.p12");
props.put("server.ssl.key-store-password", "123456");
props.put("server.ssl.key-store-type", "PKCS12");
props.put("server.ssl.key-alias", "tomcat");
new SpringApplicationBuilder(SpringBootTestApplication.class)
.properties(props).run(args);
}
}
As you can see I have commented out this:
SpringApplication.run(SpringBootTestApplication.class, args);
and used SpringApplicationBuilder class to add properties to the app.
Now, these properties are defined in the program, You can apply condition and change property values based on your requirements.
In Spring Boot, you can read and edit propeties using System.getProperty(String key) and System.setProperty(String key, String value)
public static void main(String[] args) {
// set properties
System.setProperty("server.ssl.enabled", "true");
System.setProperty("server.ssl.key-store-type", "PKCS12");
System.setProperty("server.ssl.key-store", "keys/keystore.jks");
System.setProperty("server.ssl.key-store-password", "changeit");
System.setProperty("server.ssl.key-alias", "tomcat");
// run
SpringApplication.run(DemoApplication.class, args);
}
Note:
This will overwrite the static properties (not all but the overwritten ones).
You need to define and register an ApplicationListener like this:
public class DynamicPropertiesListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
// modify the properties here, see the ConfigurableEnvironment javadoc
}
}
Now register the listener. If you run the application with the main method:
SpringApplication app = new SpringApplication(primarySources);
app.addListeners(new DynamicPropertiesListener());
app.run(args);
or, even better, create src\main\resources\META-INF\spring.factories file with this content:
org.springframework.context.ApplicationListener=x.y.DynamicPropertiesListener
Where x.y is the package of your listener.
So I have read dosens af articles on how to configure Spring boot to be aware of more yml files than application.yml and how to include these - even from subprojects. It is however hard to come by articles describing the same for "pure" Spring. I think however that i'm heading in the right direction I just can't get my configuration values back.
It's a straight forward multi-project gradle build with - for simplicity - two projects. One project is the "main" spring project - ie. Spring Context is initialized in this project. The other is a "support" module with some database entities and datasource configuration. We use annotation based configuration.
I would like to be able to define a set of configuration properties in the support module and based on whatever spring profile is active, the datasource configuration is loaded accordingly.
This SA post got me quite far following the different links in the different answers and composing my solution from this. The structure and code is as follows:
mainproject
src
main
groovy
Application.groovy
resourcers
application.yml
submodule
src
main
groovy
PropertiesConfiguration.groovy
DataSource.groovy
resources
datasource.yml
The PropertiesConfiguration.groovy adds the datasource.yml by using PropertySourcesPlaceholderConfigurer:
#Configuration
class PropertiesConfiguration {
#Bean
public PropertySourcesPlaceholderConfigurer configure() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer()
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean()
yamlPropertiesFactoryBean.setResources(new ClassPathResource("datasource.yml"))
configurer.setProperties(yamlPropertiesFactoryBean.getObject())
return configurer
}
}
The Datasource.groovy should then read values based on the spring profile using (code reduced for readability):
#Autowired
Environment env
datasource.username = env.getProperty("datasource.username")
The env.getProperty returns null. No matter what spring profile is active. I can access the configuration value using the #Value annotation, however then the active profile is not respected and it return a value even if it is not defined for that profile. My yml looks (something) like this:
---
spring:
profiles: development
datasource:
username: sa
password:
databaseUrl: jdbc:h2:mem:tests
databaseDriver: org.h2.Driver
I can from Application.groovy inspect my ApplicationContext using a debugger and confirm that my PropertySourcesPlaceholderConfigurer exist and the values are loaded. Inspecting applicationContext.environment.propertySources it is NOT there.
What am I missing?
Using a PropertySourcesPlaceholderConfigurer does not add properties to Environment. Using something like #PropertySource("classpath:something.properties") on the class level of your configuration class will add properties to Environment, but sadly this does not work with yaml-files.
So, you would have to manually add the properties read from the yaml file to your Environment. Here is one way to do this:
#Bean
public PropertySourcesPlaceholderConfigurer config(final ConfigurableEnvironment confenv) {
final PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
final YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("datasource.yml"));
configurer.setProperties(yamlProperties.getObject());
confenv.getPropertySources().addFirst(new PropertiesPropertySource("datasource", yamlProperties.getObject()));
return configurer;
}
With this code, you can inject properties in either of these two fashions:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = PropertiesConfiguration.class)
public class ConfigTest {
#Autowired
private Environment environment;
#Value("${datasource.username}")
private String username;
#Test
public void props() {
System.out.println(environment.getProperty("datasource.username"));
System.out.println(username);
}
}
With the properties supplied in the question, this will print "sa" two times.
Edit: It doesn't seem that the PropertySourcesPlaceholderConfigurer is actually needed now, so the code can be simplified to the below and still produce the same output.
#Autowired
public void config(final ConfigurableEnvironment confenv) {
final YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("datasource.yml"));
confenv.getPropertySources().addFirst(new PropertiesPropertySource("datasource", yamlProperties.getObject()));
}
Edit 2:
I see now that you are looking to use the yaml-file with multiple documents in one file, and Spring boot-style selection by profile. It does not seem to be possible using regular Spring. So I think you have to split your yaml files into several, named "datasource-{profile}.yml". Then, this should work (perhaps with some more advanced checking for multiple profiles, etc)
#Autowired
public void config(final ConfigurableEnvironment confenv) {
final YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("datasource-" + confenv.getActiveProfiles()[0] + ".yml"));
confenv.getPropertySources().addFirst(new PropertiesPropertySource("datasource", yamlProperties.getObject()));
}
Edit 3:
It could also be possible to use functionality from Spring boot without doing a full conversion of your project (I haven't actually tried it on a real project though). By adding a dependency to org.springframework.boot:spring-boot:1.5.9.RELEASE I was able to get it working with the single datasource.yml and multiple profiles, like this:
#Autowired
public void config (final ConfigurableEnvironment confenv) {
final YamlPropertySourceLoader yamlPropertySourceLoader = new YamlPropertySourceLoader();
try {
final PropertySource<?> datasource =
yamlPropertySourceLoader.load("datasource",
new ClassPathResource("datasource.yml"),
confenv.getActiveProfiles()[0]);
confenv.getPropertySources().addFirst(datasource);
} catch (final IOException e) {
throw new RuntimeException("Failed to load datasource properties", e);
}
}
How to create project architecture to support multiple envionment. Each environment will have different datasource from different property file like(dev-propertfile,test-propertyFil,Production-propertyfile) with help of spring's
org.springframework.core.env.Environment;
I'll give step by step procedure for Spring boot applications.
Inside /src/main/resources/application.properties mention spring.profiles.active=dev (or Prod)
Create /src/main/resources/application-dev.properties and give your custom dev configurations here.
Create /src/main/resources/application-prod.properties and give your custom prod configurations here.
Run.
Put property file in same location as application.property and follow
the naming convention application-{profile}.properties like
application-dev.properties,application-test.properties,
application-prod.properties
And in application.properties set spring.profiles.active=dev,test etc
For Spring Boot applications it will work easily even by using a YAML File
spring:
profiles: dev
property: this is a dev env
---
spring:
profiles: prod
property: this is a production env
---
However, for a Spring MVC application, it needs more work. Have a look at this link
Basically, it involves 2 steps
Get the Spring Profile within servlet context
If you have set the profile on the server and want it to retrieve it within your application you can use System.getProperty or System.getenv methods.
Here is the code which fetches the profile and defaults it to a local profile, if no profile has been found.
private static final String SPRING_PROFILES_ACTIVE = "SPRING_PROFILES_ACTIVE";
String profile;
/**
* In local system getProperty() returns the profile correctly, however in docker getenv() return profile correctly
* */
protected void setSpringProfile(ServletContext servletContext) {
if(null!= System.getenv(SPRING_PROFILES_ACTIVE)){
profile=System.getenv(SPRING_PROFILES_ACTIVE);
}else if(null!= System.getProperty(SPRING_PROFILES_ACTIVE)){
profile=System.getProperty(SPRING_PROFILES_ACTIVE);
}else{
profile="local";
}
log.info("***** Profile configured is ****** "+ profile);
servletContext.setInitParameter("spring.profiles.active", profile);
}
To access the application-dev.properties, say now you will need to use
#Profile("dev") at the class level
The following code will fetch the application-dev.properties and common.properties
#Configuration
#Profile("dev")
public class DevPropertyReader {
#Bean
public static PropertyPlaceholderConfigurer properties() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[] { new ClassPathResource("properties/common.properties"), new ClassPathResource("properties/application-dev.properties") };
ppc.setLocations(resources);
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
}
For accessing say application-prod.properties you have to use #Profile("prod") at the class level. More details can be found here
Take a look at Spring Profile. You will define a set of profiles configurations, like Test, Dev, Production. And then, when you launch the application, you can define wich profile it should use.
Here are some tutorials of how to use.
And this guys had the same problem as yours: How to config #ComponentScan dynamic?
We wanted a way to load different properties from application-<your_env>.properties file depending on the environment (spring profile) in a Spring MVC project, so we implemented a configuration class something like this.
#Configuration
#PropertySource({ "classpath:application-${envTarget:dev}.properties" })
#Data
public class EnvironmentConfig {
#Value("${files.s3.accessId:}")
String s3AccessId;
#Value("${files.s3.accessToken:}")
String s3AccessToken;
.
.
.
}
Then we loaded the EnvironmentConfig in the class where we needed to use it.
While running the application, you just need to pass the -DenvTarget=<your_env>, and it will pick up the application-<your_env>.properties file from src/resources folder of the project.
In the above code, it will load values from application-dev.properties files when no envTarget is specified.
Thanks to Karthikeyan Muthurangam for suggesting this clever solution.