I want to move some configuration in my property file, but don't want it to be overwritten at runtime, for security purpose. Is it possible to do it in spring boot?
Thanks,
Manish
Something like this should work, anything in secure.properties will not be able to be overridden as it will be added to the start of the env (from the properties file), regardless of what has been overridden.
#Configuration
public class SecurePropertiesConfig {
#Autowired
public void setConfigurableEnvironment(ConfigurableEnvironment env) {
try {
final Resource resource = new ClassPathResource("secure.properties");
env.getPropertySources().addFirst(new PropertiesPropertySource(resource.getFilename(), PropertiesLoaderUtils.loadProperties(resource)));
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
}
}
I tried the SecurePropertiesConfig method posted here and interestingly what is happening for me is the property value is different based on if I use a #Value annotation or just load the Environment. The #Value annotation is resolving before the setConfigurableEnvironment method resolves. So #Value will resolve to property file values set directly in the classpath while the Environment variable will have the secure (un-overridable) property value once the method is actually running. It works if I just use Environment, but unfortunately that makes it a bit brittle to a future code change accidentally using #Value instead, thinking it is "secure". I modified the SecurePropertiesConfig.java to be a ApplicationContextInitializer to force it to load the properties first.
SecureProperties.java
public class SecurePropertiesConfig implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
try {
final Resource resource = new ClassPathResource("secure.properties");
environment.getPropertySources().addFirst(new PropertiesPropertySource(resource.getFilename(), PropertiesLoaderUtils.loadProperties(resource)));
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
}
And then add the initializer in your application.properties:
context.initializer.classes=package.path.config.SecureProperties
Related
I know how I can access the application.properties values in #Service classes in Java Spring boot like below
#Service
public class AmazonClient {
#Value("${cloud.aws.endpointUrl}")
private String endpointUrl;
}
But I am looking for an option to access this value directly in any class (a class without #Service annotation)
e.g.
public class AppUtils {
#Value("${cloud.aws.endpointUrl}")
private String endpointUrl;
}
But this returns null. Any help would be appreciated.
I have already read here but didn't help.
There's no "magic" way to inject values from a property file into a class that isn't a bean. You can define a static java.util.Properties field in the class, load values from the file manually when the class is loading and then work with this field:
public final class AppUtils {
private static final Properties properties;
static {
properties = new Properties();
try {
ClassLoader classLoader = AppUtils.class.getClassLoader();
InputStream applicationPropertiesStream = classLoader.getResourceAsStream("application.properties");
applicationProperties.load(applicationPropertiesStream);
} catch (Exception e) {
// process the exception
}
}
}
You can easily achievw this by annotating ur app utils class with #component annotation . spring will take care of loading properties.
But if you don't want to do that approach , then look at the link below .
https://www.baeldung.com/inject-properties-value-non-spring-class
This may be silly question to ask but i'm unable to find any satisfactory solution to my problem. In java we don't have the concept of default variables so i am trying to give default value from properties file to my function parameters/arguments using #Value annotation, but i'm always getting null and i'm unable to figure why is this happening. Please help me to solve the issue or provide me some appropriate link/reference which may solve my issue.
MainApplication.java
#SpringBootApplication
public class Application
{
public static void main(String[] args)
{
ApplicationContext context = SpringApplication.run(NetappApplication.class, args);
Sample sample = context.getBean(Sample.class);
System.out.println(sample.check(null));
}
}
Sample.java
public interface Sample
{
public String check(String message);
}
SampleImpl.java
#Service
#PropertySource("classpath:app.properties")
public class SampleImpl implements Sample
{
#Value("${test}")
String message1;
#Override
public String check(#Value("${test}") String message)
{
return message;
}
}
app.properties
test=anand
But you are passing null to your method...
Perhaps what you want to do is to assign default value to test in case it's not defined in property file:
#Value("${test:default}");
Then, when properties are autowired by Spring if placeholder resolver doesn't get the value from props file, it will use what is after :.
The best use case for this (that I can think of) is when you create Spring configuration.
Let's say you have a configuration class: for DB access. Simply put:
#Configuration
public class DbConfig {
#Value("${url:localhost}")
String dbUrl;
// rest for driver, user, pass etc
public DataSource createDatasource() {
// here you use some DataSourceBuilder to configure connection
}
}
Now, when Spring application starts up, properties' values are resolved, and as I wrote above you can switch between value from property and a default value. But it is done once, when app starts and Spring creates your beans.
If you want to check incoming argument on runtime, simple null check will be enough.
#Value("${test}")
String message1;
#Override
public String check(String message) {
if (message == null) {
return message1;
}
}
In Spring Boot I have several options to externalize my configuration. However, how can I make such properties non-configurable, i.e. readonly.
Concretly, I want to set server.tomcat.max-threads to a fixed value and do not want somebody who is going to start the application to have the ability to change it. This could easily be done by passing it as a command line argument for instance.
It's probably not possible by default, maybe someone could suggest workarounds?
You have 2 options
Set System.setProperty("prop", "value") Property hard coded
Use properties that will override all other properties
Set system property hard coded
public static void main(String[] args) {
System.setProperty("server.tomcat.max-threads","200");
SpringApplication.run(DemoApplication.class, args);
}
Properties in secure.properties will override all others (see, Prevent overriding some property in application.properties - Spring Boot)
#Configuration
public class SecurePropertiesConfig {
#Autowired
private ConfigurableEnvironment env;
#Autowired
public void setConfigurableEnvironment(ConfigurableEnvironment env) {
try {
final Resource resource = new
ClassPathResource("secure.properties");
env.getPropertySources().addFirst(new
PropertiesPropertySource(resource.getFilename(),
PropertiesLoaderUtils.loadProperties(resource)));
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
}
I ended up implementing an ApplicationContextInitializer (Docu), which in its initialize() method simply sets the static value programatically:
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String, Object> props = new HashMap<>();
props.put(MAX_THREADS, MAX_THREADS_VAL);
environment.getPropertySources().addFirst(new MapPropertySource("tomcatConfigProperties", props));
}
Another possible solution was found here: Prevent overriding some property in application.properties - Spring Boot
I am loading my properties file as following:
#Configuration
#PropertySource("classpath:app.properties")
class MyApp {
#Bean
public PropertySourcesPlaceholderConfigurer PropertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
In the properties file, I have several database-related properties:
database.dataSource.url=jdbc:postgresql://localhost:${db-port:5432}/mydb
database.dataSource.x=...
database.dataSource.y=...
database.dataSource.z=...
Note:
${db-port} should be replaced by either the value of property/environment variable db-port or 5432. In my case, I am defining the environment variable db-port when spawning the Tomcat container.
All database-related properties are grouped under database. root. This is intentional, see below.
I want to avoid that I have to enumerate/hardcode all possible database-related properties in my code. Luckily, the database layer in use (Hikari) has the nice feature that I can pass all properties via a java.util.Properties. So, I want retrieve all defined properties under database.* and simply forward it to Hikari.
For this, I wrote the following utility:
#Component
public class PropertyFetcher
{
#Autowired
private ConfigurableEnvironment environment;
public Properties get(final String key) {
final Properties p = new Properties();
for (final PropertySource<?> s : environment.getPropertySources()) {
if (s instanceof EnumerablePropertySource) {
for (final String k : ((EnumerablePropertySource) s).getPropertyNames()) {
if (k.startsWith(key) && k.length() > key.length()) {
p.put(k.substring(key.length()), s.getProperty(k));
}
}
}
}
return p;
}
}
Now, when calling get("database."), I have all database-related properties as defined in the properties file. Great! But, the value for property dataSource.url is now
jdbc:postgresql://localhost:${db-port:5432}/mydb
instead of
jdbc:postgresql://localhost:9876/mydb
So, for some reason, the ${db-port:5432} is not resolved (yet?) when going via this route (ConfigurableEnvironment).
How can this be fixed? Or is there a better way to get all properties under a certain root without having to enumerate/hardcode them into the code?
Please note that in the default scenario, the ${db-port:5432} in property database.dataSource.url=jdbc:postgresql://localhost:${db-port:5432}/mydb is correctly resolved. I tested this by defining the following member and logging it:
#Value("${database.dataSource.url}")
final String url; // holds jdbc:postgresql://localhost:9876/mydb
You should read the property values from real environment only. Then only you will get actual or effective value of a property.
This will require a little change in your code.
change this line:
p.put(k.substring(key.length()), s.getProperty(k));
to this:
p.put(k.substring(key.length()), environment.getProperty(k));
I have a property file and using Spring property place holder, I set values to the Spring beans. Now, this property file may be modified during the run time. Is there a way to refresh the properties of the Spring beans with this newly modified property value? Especially, I have many singleton beans? How can I refresh them with the new values? Is there already a solution to this or should it be custom coded? If it doesn't already exist, can someone please give the best approach to achieve this? Thanks!
PS: My application is a batch application. I use Spring based Quartz configuration to schedule the batches.
I'll leave this in for reference, but the updated answer is below the divider:
Well the ConfigurableApplicationContext interface contains a refresh() method, which should be what you want, but the question is: how to access that method. Whichever way you do it, you'll start with a bean that has a dependency of type ConfigurableApplicationContext:
private ConfigurableApplicationContext context;
#Autowired
public void setContext(ConfigurableApplicationContext ctx){
this.context = ctx;
}
Now the two basic options I'd suggest would be to either
use the Task Execution Framework and let your bean watch the property resources regularly, refreshing the ApplicationContext when it finds changes or
expose the bean to JMX, allowing you to manually trigger the refresh.
Referring to comments: since it seems impossible to refresh the entire context, an alternative strategy would be to create a properties factory bean and inject that into all other beans.
public class PropertiesFactoryBean implements FactoryBean<Properties>{
public void setPropertiesResource(Resource propertiesResource){
this.propertiesResource = propertiesResource;
}
private Properties value=null;
long lastChange = -1L;
private Resource propertiesResource;
#Override
public Properties getObject() throws Exception{
synchronized(this){
long resourceModification = propertiesResource.lastModified();
if(resourceModification != lastChange){
Properties newProps = new Properties();
InputStream is = propertiesResource.getInputStream();
try{
newProps.load(is);
} catch(IOException e){
throw e;
} finally{
IOUtils.closeQuietly(is);
}
value=newProps;
lastChange= resourceModification;
}
}
// you might want to return a defensive copy here
return value;
}
#Override
public Class<?> getObjectType(){
return Properties.class;
}
#Override
public boolean isSingleton(){
return false;
}
}
You could inject this properties bean into all your other beans, however, you would have to be careful to always use prototype scope. This is particularly tricky inside singleton beans, a solution can be found here.
If you don't want to inject lookup methods all over the place, you could also inject a PropertyProvider bean like this:
public class PropertiesProvider implements ApplicationContextAware{
private String propertyBeanName;
private ApplicationContext applicationContext;
public void setPropertyBeanName(final String propertyBeanName){
this.propertyBeanName = propertyBeanName;
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException{
this.applicationContext = applicationContext;
}
public String getProperty(final String propertyName){
return ((Properties) applicationContext.getBean(propertyBeanName)).getProperty(propertyName);
}
}