I am trying to run Quartz Scheduler using SpringBoot. Using Quartz Jdbc Data Store. Due to security reasons , we wish to pick the Db credentials from the properties file. From what I understand from here(Using env variable in Spring Boot's application.properties) and springBoot docs, SpringBoot automatically replaces environment variables in application.properties but I am not seeing this . Here is my system environment file which i am sourcing before running the application
export DB_HOST=localhost
export DB_PORT=11111
export DB_USER=root
export DB_PASSWORD=root
export QUARTZ_DB_NAME=quartz
And here is my application.properties
org.quartz.dataSource.quartzDataSource.URL =jdbc:mysql://${DB_HOST}:${DB_PORT}/${QUARTZ_DB_NAME}
org.quartz.dataSource.quartzDataSource.user = ${DB_USER}
org.quartz.dataSource.quartzDataSource.password = ${DB_PASSWORD}
And my configuration class
#Configuration
public class ConfigureQuartz {
#Autowired
private ApplicationContext applicationContext;
#Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException
{
final SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();
quartzScheduler.setSchedulerName("mdsScheduler");
quartzScheduler.setQuartzProperties(quartzProperties());
final AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
quartzScheduler.setJobFactory(jobFactory);
return quartzScheduler;
}
#Bean
public Properties quartzProperties() throws IOException {
final PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/application.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
But when i run my spring application using java -jar <>.java , I dont see the values substitued .
I can workaround by reading the values using System.getEnv() but would be great if the values can be substitued . Not sure why its not working :(
Spring-boot allows us several methods to provide externalized configurations , you can try using application.yml or yaml files instead of the property file and provide different property files setup according to different environments.We can separate out the properties for each environment into separate yml files under separate spring profiles.Then during deployment you can use :
java -jar -Drun.profiles=SpringProfileName
to specify which spring profile to use.Note that the yml files should be name like
application-{environmentName}.yml
for them to be automatically taken up by springboot.
Reference : https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties
To read from the application.yml or property file :
The easiest way to read a value from the property file or yml is to use the spring #value annotation.Spring automatically loads all values from the yml to the spring environment , so we can directly use those values from the environment like :
#Component
public class MyBean {
#Value("${name}")
private String name;
// ...
}
Or another method that spring provides to read strongly typed beans is as follows:
YML
acme:
remote-address: 192.168.1.1
security:
username: admin
roles:
- USER
- ADMIN
Corresponding POJO to read the yml :
#ConfigurationProperties("acme")
public class AcmeProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
public boolean isEnabled() { ... }
public void setEnabled(boolean enabled) { ... }
public InetAddress getRemoteAddress() { ... }
public void setRemoteAddress(InetAddress remoteAddress) { ... }
public Security getSecurity() { ... }
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
public String getUsername() { ... }
public void setUsername(String username) { ... }
public String getPassword() { ... }
public void setPassword(String password) { ... }
public List<String> getRoles() { ... }
public void setRoles(List<String> roles) { ... }
}
}
The above method works well with yml files.
Reference:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
To read from the environment :
If you are running the application on linux set the env as below :
export DB_HOST=localhost
export DB_PORT=11111
export DB_USER=root
export DB_PASSWORD=root
export QUARTZ_DB_NAME=quartz
if you are on windows set it like :
SET DB_HOST=localhost
SET DB_PORT=11111
SET DB_USER=root
SET DB_PASSWORD=root
SET QUARTZ_DB_NAME=quartz
In your application.properties if you keep the keys as below , it should automatically resolve the value from the environment :
spring.datasource.url = ${DB_HOST}/"nameofDB"
spring.datasource.username = ${DB_USER}
spring.datasource.password = ${DB_PASSWORD}
Related
If I have some global config properties value that want to set on application start up, one of the ways to do is by setting it in application.properties and then using #Value to inject those values. However, if I want to set the values by making an API call to get those properties value on application start up and then set the values (but want to use similar way as #Value), rather than getting and setting it via properties files, how should it be achieved ?
#Configuration
public class config {
#Value("${properties1}")
private String properties1;
#Value("${properties2}")
private String properties2;
}
I have done some web search on custom property source (https://projects.spring.io/spring-cloud/spring-cloud.html#customizing-bootstrap-property-sources), and tried to follow the example, but encountered the error that the placeholder could not be resolved. How to get back the value ?
Could not resolve placeholder 'property.from.sample.custom.source' in value "${property.from.sample.custom.source}"
#Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {
#Override
public PropertySource<?> locate(Environment environment) {
return new MapPropertySource("customProperty",
Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
}
}
#Service
public class MainService {
#Value("${property.from.sample.custom.source}")
private String value;
public void printValue() {
System.out.println("value - " + value);
}
}
Assuming you use Spring Boot, you can run the Spring Boot application and pass the arguments using the following Maven command (depends on the Spring Boot version):
Spring Boot 1.x: using -Drun.arguments:
spring-boot:run -Drun.arguments=--properties1=One,--properties2=Two
Spring Boot 2.x: using -Dspring-boot.run.arguments:
spring-boot:run -Dspring-boot.run.arguments=--properties1=One,--properties2=Two
Now you can access the values using the #Value annotation:
#Value("${properties1}")
private String properties1;
#Value("${properties2}")
private String properties2;
Note: Once the properties are defined in the properties files (ex. application.properties and/or application-dev.yml etc..), defined in the command line like above, they can be accessed through the #Value annotation.
Baeldung's website offers a nice article: Command-Line Arguments in Spring Boot.
I am not sure if we can set values to properties file after application is up. Because properties file will be injected to #Bean when application is running. But we can hack this.
First, Create a file that contains all the configuration file which will be load from properties file. This file will be our template and initial / default values of the configuration.
Let say we have below configuration as application.yml
config:
name: Anna
age: 18
Then create configuration file
#ConfigurationProperties(prefix = "config")
public class ConfigProperties {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
Don't forget to modify our Application class
#SpringBootApplication
#EnableConfigurationProperties(ConfigProperties::class)
public class Application { ... }
Through here, we can just #Autowire the config class to get the value we want, rather than using a #Value.
Through here we already have the properties value to our config class. Then if you want to update the properties value (now already on config file) through an API, do a simple setName(...).
But, we have to be aware that this way is only work on a single run. When we restart the application, the value will use the default from properties file.
I have application.properties file:
value=a
Then I would like to load a property file based on that value - a.properties and read/use properties from that file.
I was thinking about something like this:
#Configuration
public class PropertiesConfiguration {
#Value("${value}")
private String value;
#Bean
public PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocation(new ClassPathResource(value + ".properties"));
return configurer;
} }
but the value is always null for some reason. When I try to get that value e.g. in service/component then it works fine. I would like to avoid using spring profiles. Any ideas how to achieve that with latest Spring?
In the line configurer.setLocation(new ClassPathResource(value + ".properties")); it should be "application.properties" since your file name is application.properties
Also in the properties file define it as
value = a
with spaces
One of the solutions I have come across and works fine is use of Component and PropertySource annotations.
#Component
#PropertySource(value = "classpath:${value}.properties")
public class CountryService implements ICountryService {
#Value("${<whatever in a.properties file>}")
private String currency;
#Override
public String getCurrency() {
return currency;
}
}
where ${value} is taken from application.properties. Then Autowire that bean whenever needed.
I have a little SpringBoot Application, which can execute different functions via OpenLdap.
getUser
createUser
deleteUser
etc.
That works fine. Now i want to create an application.Yml, where i can manage different environments with different credentials. I read some tutorials, but i still have some understanding problems. Actually my code looks like that:
UserController:
...
protected static String serverURL = "xxxxx:90xx";
protected static String LdapBindDn = "cn=admin, xxxxx";
protected static String LdapPassword = "xxxx";
...
#RequestMapping(value = "/{userid:.+}",method = RequestMethod.GET,consumes="application/json",produces = "application/json")
public UserData getUser(#PathVariable String userid) {
DirContext context = connectToLdap();
//some operations...
context.close();
return user;
}
... // same for the other functions
My plan is now, that i want to specify the credentials in an extra application.yml instead of at the beginning of the UserController (see above).
Then i have created an application.yml in the src/main/resources:
# Actual environment
spring:
profiles.actives: development
---
# Dev Profile
spring:
profiles: dev
datasource:
serverUrl: ldaps://xxxxxx:90xx
AdminName: xxxx
AdminPassword: xxxxxx
BaseDN: xxxxx
---
# Production Profile
spring:
profiles: prod
datasource:
serverUrl: ldaps://xxxx2:90xx
AdminName: xxxxx2
AdminPassword: xxxxx2
BaseDN: xxxxxx
Now i need to call this configuration. I have read in one tutorial (http://therealdanvega.com/blog/2017/06/26/spring-boot-configuration-using-yaml) that i have to create an extra class "ApplicationProperties" for the properties of the .yml file.
#Component
#ConfigurationProperties("datasource")
public class ApplicationProperties {
private String serverURL;
private String adminName;
private String adminPassword;
private String baseDN;
// Getter-/Setter-Methods
}
Now i need to define my variables from the beginning with the values from the .yml, right? I went back to my UserController and tried something like that:
private String serverURL;
private String adminName;
private String adminPassword;
private String baseDN;
#Autowired
ApplicationProperties appProp;
#RequestMapping(value = "/{userid:.+}",method = RequestMethod.GET,consumes="application/json",produces = "application/json")
public UserData getUser(#PathVariable String userid) {
DirContext context = connectToLdap();
//some operations...
context.close();
return user;
}
... // same for the other functions
private DirContext connectToLdap(){
System.out.prinln(appProp.getServerURL());
System.out.prinln(appProp.getAdminName());
System.out.prinln(appProp.getAdminPassword());
.... // Code for the Ldap connection
}
But the variable "appProp" is still empty. I know, that here is somewhere a big understanding problem. I don't know how to call these properties from the .yml file.
Thanks for every help in advance!
You can get properties from your .yml file by creating a config class:
application.yml
datasource:
serverUrl: ldaps://xxxxxx:90xx
adminName: xxxx
adminPassword: xxxxxx
baseDN: xxxxx
ApplicationConfig.java class :
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "datasource")
public class ApplicationConfig {
private String serverUrl;
private String adminName;
private String adminPassword;
private String baseDN;
//getters setters
}
Then you can call your ApplicationConfig class
#Autowired
public ApplicationConfig app;
public UserData getUser(#PathVariable String userid) {
DirContext context = connectToLdap();
//some operations...
appProp.getServerUrl();
appProp.getAdminName();
context.close();
return user;
}
And I recommend you to create profile based properties as spring boot picks them automatically, like application-{profile}.{properties|yml}
You can create application-production.yml file and set your profile by adding #Profile("production") annotation in your class.
I have add some config inside my application.yml file and I want to read it from my Java code.
The added node inside the YAML file looks like this:
myConfig:
projectOne:
mantisID: 501
user: username
password: passwd
projectTwo:
mantisID: 502
user: username
password: passwd
What I want is to get a List of Project objects where
Project.mantisID = 501,
Project.user = "username",
Project.password = "passwd",
etc...
I know spring can read this file with some #Value annotation but how can I use this in order to get what I need?
You can use #ConfigurationProperties annotation to map your configuration to a Bean, then you'll be able to inject your Bean anywhere and fetch those properties.
To do so, first create a class which represents the data structure in your configuration. Then annotate it with #ConfigurationProperties and #Configuration annotations.
#Configuration
#ConfigurationProperties
public class MyConfig {
private final Map<String, Project> myConfig = new HashMap<>();
public Map<String, Project> getMyConfig() {
return myConfig;
}
public static class Project {
private String mantisID;
private String password;
private String user;
// Getters and setters...
}
}
Note that getters and setters are required in the Project class. Also keep in mind that naming of getters and setters is important here.
After you have setup this class, you can inject it anywhere in your project and access its properties.
#Service
public class SomeService {
private final Map<String, MyConfig.Project> projects;
#Autowired
public SomeService(MyConfig config) {
this.projects = config.getMyConfig();
projects.get("projectOne").getMantisID();
projects.get("projectTwo").getPassword();
}
}
You can read more about this here.
Just to finish, I answered myself to my second question.
This is what my service looks like now :
#Service
public class MantisProjectService {
private final Map<String, MantisProjectConfiguration.Project> projects;
private List<MantisProjectConfiguration.Project> mantisProjects = new ArrayList<>();
#Autowired
public MantisProjectService(MantisProjectConfiguration mantisProjectConfiguration)
{
this.projects = mantisProjectConfiguration.getMantisProjectConfiguration();
for (Map.Entry<String, MantisProjectConfiguration.Project> project : projects.entrySet())
{
MantisProjectConfiguration.Project mantisProject = project.getValue();
mantisProject.setName(project.getKey());
mantisProjects.add(mantisProject);
}
}
public List<MantisProjectConfiguration.Project> getMantisProjects()
{
return mantisProjects;
}
}
It returns a List of all the projects. And it is awesome! =)
I have a properties file containing values like jdbc.password={enc}laksksjdjdj
Using JDK 1.7 and Spring 4.1.5 my configuration class looks like this
#PropertySources({
#PropertySource("classpath:application.properties"),
#PropertySource("classpath:env.properties")
})
#ComponentScan("com.acme")
#Configuration
public class SpringConfig
{
#Autowired
private ConfigurableEnvironment env;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
{
return new EncryptedPropertySourcesPlaceholderConfigurer();
}
}
What I am trying to achieve is to translate any values in my properties file from an encrypted value to the actual value. Some values will be encrypted and some won't. This is what I have attempted so far and when I place a breakpoint on convertProperties() the props argument is always empty. I can't make any sense of this because I can see that at this point this.environment is loaded with all the properties from the files.
public class EncryptedPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer
{
#Override
protected void convertProperties(Properties props)
{
Enumeration<?> propertyNames = props.propertyNames();
while (propertyNames.hasMoreElements()) {
String propertyName = (String) propertyNames.nextElement();
String propertyValue = props.getProperty(propertyName);
// String convertedValue = <translate the value>;
props.setProperty(propertyName, convertedValue);
}
}
#Override
protected Properties mergeProperties() throws IOException {
final Properties mergedProperties = super.mergeProperties();
convertProperties(mergedProperties);
return mergedProperties;
}
}
Has anyone been able to achieve this using PropertySourcesPlaceholderConfigurer? I have similar logic working in older applications using PropertyPlaceholderConfigurer but wanted to use the newer Spring configuration.
I noticed that Jasypt has a similar EncryptablePropertySourcesPlaceholderConfigurer but this behaves in the same fashion, so I'm confused.
For some reason using the #PropertySources annotation(s) does not work as expected.
When EncryptedPropertySourcesPlaceholderConfigurer.convertProperties() is called the autowired ConfigurableEnvironment does not contain the entries from my properties files. There are no references to my listed properties files at all.
To get around this limitation I removed the annotations and explicitly loaded these resources in in the config class. So it now reads like this
#ComponentScan("com.acme")
#Configuration
public class SpringConfig
{
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
{
EncryptedPropertySourcesPlaceholderConfigurer p = new EncryptedPropertySourcesPlaceholderConfigurer(new KeyfileDecryptor());
p.setLocations(
new ClassPathResource("application.properties"),
new ClassPathResource("env.properties")
);
return p;
}
#Bean
public DataSource dataSource(
#Value("${jdbc.driverclassname}") String driverclassname,
#Value("${jdbc.url}") String url,
#Value("${jdbc.username}") String username,
#Value("${jdbc.password}") String password)
{
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverclassname);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
This change introduced the issue that the autowired ConfigurableEnvironment will be null so I had to change to using #Value injection in order to configure my dataSource from the properties.
Everything now works as expected and EncryptedPropertySourcesPlaceholderConfigurer.convertProperties() receives all of the properties values from both files.
Written one sample application to do add support for using Encrypted values here
https://github.com/pushpendra-jain/spring-examples/tree/master/PropertySourcesPlaceholderEncrypter
Basically instead of trying to invoke its convertProperty method overrided its processProperties method and getting the job done and code surely does not change any existing behavior.
see https://github.com/pushpendra-jain/spring-examples/blob/master/README.md for steps.