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.
Related
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
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);
}
}
Followed this tutorial https://developer.ibm.com/messaging/2018/04/03/mq-jms-spring-boot/ and developed a Spring Boot JMS applicatin which sends a message to IBM MQ. (used this dependency - mq-jms-spring-boot-starter).
As per the tutorial, the configuration properties (Queue Manager, Channel, Port etc) can be given in application.yml/ application.properties file as below, and JmsTemplate will automatically be configured with the properties.
ibm.mq.queueManager=QM1
ibm.mq.channel=SYSTEM.DEF.SVRCONN
ibm.mq.connName=server.example.com(1414)
ibm.mq.user=user1
ibm.mq.password=passw0rd
The application works perfect and it sends message to the MQ now this way.
But I want to set the properties inside the class, not from the properties file (reading from a database or something). How to set these values inside the class?
You can use a customizer method on the CF after the initial properties have been populated.
In the Application class, this code allows additional properties to be configured:
#Bean
public MQConnectionFactoryCustomizer myCustomizer() {
MQConnectionFactoryCustomizer c = new MQConnectionFactoryCustomizer() {
#Override
public void customize(MQConnectionFactory factory) {
factory.setXXXX(property, value);
}
};
return c;
}
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);
}
}
In an application ther are multiple properties file for managing exception messages , alerts , and some others text these file like this :
- core-message.properties
- databaseException.properties
......
in Service layer maybe a database call occure and the database return a key that exist in one the properties files , and i want get the value and raise the exception messsage to user interface layer .
if i know that the key in wich properties file the code will be like this :
#Value("#{core['theExceptionKey']}")
public String excpetionMessage;
private void myMethod() {
throw new ExceptionClass(exceptionMessage);
}
i think spring can do that because when i use spring:message tag in jsp files spring does not know the key in witch file but it load the message correctly.
You can use Spring Environment abstraction for that.
First you need to add Property Source to your Java Configuration file
#Configuration
#PropertySource("classpath:/com/mypacakge/core-message.properties")
public class AppConfig {
Or if you have multiple properties files
#Configuration
#PropertySources({
#PropertySource("classpath:core-message.properties"),
#PropertySource("classpath:database.properties")
})
public class AppConfig {
Add PropertySourceConfigurer to to your Java Configuration file
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Now let's say that in your core-message.properties you have the following data
message.name=Hello
You can retrieve this data in any bean by autowiring Environment abstraction and then calling env.getProperty()
#Autowired
Environment env;
public void m1(){
String message = env.getProperty("message.name")` // will return Hello
Environment object provides interface to configure property sources and resolve properties. It provides convenience to read from a variety of sources: properties files, system environment variable, JVM system properties, servlet context parameters, and so on, which is very useful. For example :
environment.getSystemProperties().put("message", "Hello");
System.getProperties().put("message", "Hello");
environment.getSystemProperties().get("message"); // retrieve property
environment.getPropertySources() // allows manipulation of Properties objects
Spring Reference Documentation - Environment
To get the value of the key programmatically you can use the following:
#Autowired
private Environment env;
...
String something = env.getProperty("property.key.something");