creating custom PropertySourcesPlaceholderConfigurer using property loaded dynamically - spring

I am facing some challenge creating PropertySourcesPlaceholderConfigurer based on some value that is available in another property file.
I have a property file, custom-{environment}.property, which contains a value, that is needed to set location of PropertySourcesPlaceholderConfigurer.
My CustomConfiguration looks something like:
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setLocation(customLocation);
//Custom url location based on a String available in the properties file. This is the problem area
return propertySourcesPlaceholderConfigurer;
}
I want to populate this customLocation from the properties file. Tried autowiring Environment, but it's failing as environment is null when placeholderConfigurer() is getting called. Tried using #PropertySource("custom-${environment}.property") and then #Value("**customLocation**"), but that's also not working.
Please let me know how this can be done. Thanks in advance.

I would suggest adding an ApplicationContextInitializer to load your property files instead of a plain #PropertySource. First load your custom-{environment}.properties next your configurable properties file.
public class PropertySourceInitializer implements ApplicationContextInitializer {
private static final String DEFAULT_CONFIG_FILE = "classpath:custom-${environment}.properties";
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
final ConfigurableEnvironment env = applicationContext.getEnvironment();
final MutablePropertySources mps = env.getPropertySources();
//
Resource resource = applicationContext.getResource(env.resolvePlaceholders(DEFAULT_CONFIG_FILE));
mps.addLast(new ResourcePropertySource(resource.getDescription(), resource));
String additional = env.getProperty("name.of.property");
if (StringUtils.hasText(additional) {
Resource additionalResource = applicationContext.getResource(env.resolvePlaceholders(additional));
if (additionalResource.isReadable() ) {
mps.addLast(new ResourcePropertySource(resource.getDescription(), resource));
}
}
}
}
Trying to get it to work with a #PropertySource will be much harder as the phases in which the PropertySourcesPlaceHolderConfigurer is created is different then the one in which the #PropertySource annotations are scanned. Staged loading of #PropertySource (which is basically what you want) is quite difficult. Spring Boot also has its own loading mechanism (which actually is also a ApplicationContextInitializer.

Can you try setting your location as a system property?
#Value("#{ systemProperties['myapp.location'] }")
private String location;
You need to set "myapp.location" as system property.

Related

Retrieve YML / YAML properties

I have a external yaml properties files that I have loaded and I want to retrieve. But the suggested way to get YAML is like so:
#Value("${some.var}");
and this isn't working.
I am loading the files in like so:
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
String userHome = System.getProperty('user.home');
ArrayList<String> locations = new ArrayList<String>(
Arrays.asList(
"${userHome}/.boot/beapi_server.yml",
"${userHome}/.boot/beapi.yml",
"${userHome}/.boot/beapi_db.yml",
"${userHome}/.boot/beapi_api.yml"
)
);
Collections.reverse(locations);
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
for (String location : locations) {
String finalLocation = location.toString();
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new FileSystemResource(finalLocation));
propertySourcesPlaceholderConfigurer.setProperties(Objects.requireNonNull(yaml.getObject()));
}
return propertySourcesPlaceholderConfigurer;
}
The actuator/configprops also don't show the properties.
What am I doing wrong? A bit frustrated.
Ok so I figured it out and wanted to post the answer for others to see.
After loading the external files into your PropertySources, you can now access them through #ConfigurationProperties annotation by referencing the YML structure prefix head and then just access the properties directly through assignment
// 'tomcat' if the top level prefix in my yml file
#ConfigurationProperties(prefix="tomcat")
class something{
ArrayList jvmArgs
}
Now lets look at my yml:
tomcat:
jvmArgs:
-'-Xms1536m'
-'-Xmx2048m'
-'-XX:PermSize=256m'
-'-XX:MaxPermSize=512m'
-'-XX:MaxNewSize=256m'
-'-XX:NewSize=256m',
-'-XX:+CMSClassUnloadingEnabled'
-'-XX:+UseConcMarkSweepGC'
-'-XX:+CMSIncrementalMode'
-'-XX:+CMSIncrementalPacing'
-'-XX:CMSIncrementalDutyCycle=10'
-'-XX:+UseParNewGC'
-'-XX:MaxGCPauseMillis=200'
-'-XX:MaxGCMinorPauseMillis=50'
-'-XX:SurvivorRatio=128'
-'-XX:MaxTenuringThreshold=0'
-'-server'
-'-noverify'
-'-Xshare:off'
-'-Djava.net.preferIPv4Stack=true'
-'-XX:+EliminateLocks'
-'-XX:+ExplicitGCInvokesConcurrent'
-'-XX:+UseBiasedLocking'
-'-XX:+UseTLAB'
And we see this will directly assign the value jvmArgs to the ArrayList JvmArgs in the class above. Simple.
So, rather simple and elegant solution once you know how... but knowing how is the trick isn't it :)
i'm using yaml configuration too. in my case, you have to create a configuration class then you can autowired it.
this is my code:
PdfConfig.java
#Component
#ConfigurationProperties(prefix = "pdf")
#EnableConfigurationProperties
public class PdfConfig {
private String licensePath;
private Watermark watermark;
// setter getter goes here
}
PdfServiceImpl.java
#Service
public class PdfServiceImpl implements PdfService {
#Autowired
private PdfConfig pdfConfig;
}
application-local.yml
#### Pdf Config ####
pdf:
license-path: classpath:license/development/itextkey.xml
watermark:
image-path: classpath:static/img/logo.png
opacity: 0.2f
enabled: true
If you want to learn about setting spring yaml, you can go to this link spring-yaml
Hope this helped!

Reading applicatiion.properties once in a singleton class

I have a singleton configuration class where I want to store all the properties for our web application.
How do we read in the application.properies file like any other properties file without using annotations?
What is the the fully qualified filename for application.properies i.e. /application.properies?
We only want to read application.properties once.
Spring boot already reads all the properties stored in application.properties and much more, read Externalized Configuration documentation.
If you want to map one property named server.port you can just use #Value("${server.port}") Integer port.
If you want to access to all the properties loaded by Spring Boot, you can use the Environment object and access to all loaded PropertySources and retrieve all values from each property source.
In this this answer shows how. However, to avoid losing the precedence order of loaded properties, you have to reverse the property source list. Here you can find the code to load all the properties without losing the spring precedence order:
#Configuration
public class AppConfiguration {
#Autowired
Environment env;
public void loadProperties() {
Map<String, Object> map = new HashMap();
for (Iterator it = ((AbstractEnvironment) env).getPropertySources().iterator().reverse(); it.hasNext(); ) {
PropertySource propertySource = (PropertySource) it.next();
if (propertySource instanceof MapPropertySource) {
map.putAll(((MapPropertySource) propertySource).getSource());
}
}
}
}

How to use systemproperties to get property by using Spring EL and #Value()

#Configuration
#PropertySource("classpath:test.properties")
public class Config {
#Bean
public CompactDisc cd(#Value("#{ systemProperties['artist']}") String artist) {
HotelCalifornia hotelCalifornia = new HotelCalifornia();
hotelCalifornia.setArtist( artist);
return hotelCalifornia;
}
#Bean
public CdPlayer player(CompactDisc cd) {
CdPlayer player = new CdPlayer();
player.setCd(cd);
return player;
}
}
The property is in the test.properties file. I cant get the property "artist" from the systemProperties.But I can get it if i use #autowired to instantiate a environment bean.How can i deal with it?
you dont have to do anything. the spring recognizes property is first it will check in the system properties, then the class level properties and then properties initialized using property placeholder.
so try using #Value("${artist}") provided you have artist set in the systems property somehow.

What configuration enables the evaluation of #Value annotations?

I'm tying to do a very minimal programmatic/annotation based configuration of Spring, to do some command line stuff and I want to be able to inject value of some bean values from System properties.
I'm using the #Value like this:
#Value("${MigrateDb.task:default}")
private String task;
It's sort of working, but it's not evaluating the value definition, I'm just getting "${MigrateDb.task:default}" in the actual field, instead of Spring evaluating it and giving me the value of the Migrate.db.task system property (or default).
What do I need to add to my Configuration class to enable this behaviour?
try using it this way:
#Value("${MigrateDb.task:default}")
private String task;
XML Config:
<context:property-placeholder
location="your.filelocation.properties" />`
Java Config :
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
propertyPlaceholderConfigurer.setLocation(new ClassPathResource("file.properties"));
return propertyPlaceholderConfigurer;
}
From ShadowRay's answer, the minimum code to enable the requested behaviour is:
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer(){
return new PropertyPlaceholderConfigurer();
}
Method should be static as per: https://stackoverflow.com/a/14943106/924597

Customize #PropertyResource handling behavior for java annotation based configuration

I wish to customize the handling of the property source, while using java annotation based intializing a spring web application.
#Configuration
#PropertySource("ldap.properties")
#Repository
public class LdapDao {
...
#Autowired
public void setEnv(Environment env) throws NamingException {
this.url = env.getProperty("url").trim();
this.user = env.getProperty("user").trim();
this.password = env.getProperty("password).trim();
this.initializeLdapContext();
}
...
}
In this case, spring will look for the property source on classpath. If the property source is declared as:
#PropertySource("file:/${conf.dir}/ldap.properties")
ldap.properties is searched under the directory specified by the system property "conf.dir".
I need the behavior where the property resource is first searched under the directory specified by the system property "conf.dir". If it is not found there, its location defaults to classpath.
Any suggestion on how to achieve this behavior?
Use this
#PropertySource({"ldap.properties", "file:/${conf.dir}/ldap.properties"})
and you will get last one, just add in yours propConfig code
PropertySourcesPlaceholderConfigurer propConfig = new PropertySourcesPlaceholderConfigurer();
//.....
propConfig.setIgnoreResourceNotFound(true);
propConfig.setIgnoreUnresolvablePlaceholders(false);
propConfig.setLocalOverride(true);
//....

Resources