Prefix properties of multiple PropertyPlaceholderConfigurers - spring

I want to use Spring's PropertyPlaceholderConfigurer to read two property files. I can load each of them by using one of the following tags:
<context:property-placeholder location="class path:com/myapp/internal.properties"/>
<context:property-placeholder location="file://${settings.location}/external.properties"/>
I am not allowed to change the keys in those two files. Both files may contain entries with the same key.
I need to inject the value of a specific file.
//Pseudocode of injecting a property of a specific file
#Value("${internal.properties:my.key}")
String internalValue;
#Value("${external.properties:my.key}")
String externalValue;
So how to specify the file, and not only the key?

you will have to translate it to xml if needed:
public class InternalPropertyPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer{
public UploaderPropertyPlaceholderConfigurer() {
setLocations(new ClassPathResource[]{
new ClassPathResource("com/myapp/internal.properties"),
});
setPlaceholderPrefix("$internal{");
setPlaceholderSuffix("}");
}
and register it in spring ( or use #Component in above class ):
#Bean
public InternalPropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new InternalPropertyPlaceholderConfigurer();
}
this way you should be able to inject properties with this rather ugly syntax:
#Value("$internal{your.key}")
private String value;
If it works, then just add 2nd bean for external :)

Related

Loading String[] from properties file into origins field of #CrossOrigin using property placeholder expression

In my spring boot application I have the following controller
#RestController(value = "ProjectController")
#CrossOrigin(origins = {"${app.api.settings.cross-origin.urls}"})
public class ProjectController {
// some request mapping methods
}
The property app.api.settings.cross-origin.urls is already a key having comma separated valid urls in application.properties file like
app.api.settings.cross-origin.urls=http://localhost:3000, http://localhost:7070
This approach works till I have only single value like
app.api.settings.cross-origin.urls=http://localhost:3000 but not for comma separated values.
The origins field inside #CrossOrigin is of type String[] still it does not convert into String[] automatically.
I mean there should be some way to achieve this provided by the framework. Not a work around.
I can achieve using comma separated urls from properties files using #Value into a List<String> or String[] as a field inside a #Configuration class like below
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Value("${app.api.settings.cross-origin.urls}")
private String[] consumerUiOrigins;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/api/**")
.allowedOrigins(consumerUiOrigins);
}
}
But this would be a global configuration having application wide applicability. I want to stick to the more fine grained #CrossOrigin annoation based CORS configuration.
So I put my question clearly below.
Is it possible to inject comma separated value from properties file as String[] using property plcaholer expression (${*}) into spring annotation fields having the same type i.e. String[] ????? If yes then how??? If no then can we tweak some core framework classes to achieve this??? Anyone please help....
P.S. - Please do not mark my question as duplicate of Use Spring Properties in Java with CrossOrigin Annotation or in Spring-Config XML
My question is more on usage of property placholder expressions inside spring annotation fields having multi element type like String[] and less on the configuration of CORS in spring applications.
Try doing as below in application.properties:
app.api.settings.cross-origin.urls="http://localhost:3000","http://localhost:7070"

how to load property file in to spring boot project with annotations?

I have written queries in property file. I want to read the property file in to one class with annotations in spring boot. How can i read it? And is there any better approach for writing queries in spring boot project?
If you add your properties in application.properties file, you can read them inside the spring boot classes like:
#Service
public class TwitterService {
private final String consumerKey;
private final String consumerKeySecret;
#Autowired
public TwitterService(#Value("${spring.social.twitter.appId}") String consumerKey, #Value("${spring.social.twitter.appSecret}") String consumerKeySecret) {
this.consumerKey = consumerKey;
this.consumerKeySecret = consumerKeySecret;
} ...
You can annotate fields in your components by #Value("${property.name}")
Else, you can use Properties Object in java.util package.
For example, i have a mode property, which values are dev or prod, i can use it in my beans as follow :
#Value("${mode:dev}")
private String mode;
The other approach is by using :
Properties pro = new Properties();
pro.load(this.getClass().getClassLoader().getResourceAsStream());
You can use #PropertySource to read the properties from a file and then pass them to a bean. If you have a file called "queries.properties" that has a property like:
query1: select 1 from foo
Then your config might look like:
#PropertySource("classpath:queries.properties")
#Configuration
public class MyConfig {
#Bean
public DbBean dbBean(#Value("${queries.query1}") String query) {
return new DbBean(query);
}
}

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

Spring: #NestedConfigurationProperty List in #ConfigurationProperties

Hi I am trying to get the following configuration up and running.
#ConfigurationProperties(prefix="my")
public class Config {
#NestedConfigurationProperty
private List<ServerConfiguration> servers = new ArrayList<ServerConfiguration>();
public List<ServerConfiguration> getServers() {
return this.servers;
}
}
#ConfigurationProperties(prefix = "server")
public class ServerConfiguration {
private String name;
private String description;
}
So, I want to have multiple server configs nested in objects.
I tried setting the properties with the following properties file. I can see that the list is added up by items but the members of the server are never set (name, description)
my.servers[0].name=test
my.servers[0].server.name=test
my.servers[1].name=test
my.servers[1].server.name=test
To extend what Maciej said already.
#ConfigurationProperties should be set only on root objects (that is objects that are responsible to handle a given prefix. There is no need to annotated nested objects with it.
#NestedConfigurationProperty is only used by the meta-data generator (to indicate that a property is not a single value but something we should explore to generate additional meta-data. In the case of the List there isn't any finite amount of properties so the meta-data will have to stop at the list.
In any case you need a getter and a setter for each singular property. We don't do field binding and we require a public getter to avoid exposing unnecessary properties in the meta-data.
You need to add setters and getters to ServerConfiguration
You don't need to annotate class with nested properties with #ConfigurationProperties
There is a mismatch in names between ServerConfiguration.description and property my.servers[X].server.name=test

Using #Value to inject property to constructor, SpelEvaluationException: EL1008E (4.1.6)

I read a post about authentication with facebook, http://www.jasha.eu/blogposts/2013/09/retrieve-facebook-profile-data-java-spring-social.html
he use autowired to inject 3 arguments to the constructor. which gives me SpelEvaluationException.
My project, I add a config.properties under /src/
facebook.clientId=237473571343
facebook.clientSecret=9iuwijig[sa[w90u2tjgjgj
application.host=http://localhost:8080
and the constructor
#Controller
public class FacebookSpringSocialAuthenticator {
public static final String STATE = "state";
private String applicationHost;
private FacebookConnectionFactory facebookConnectionFactory;
#Autowired
public FacebookSpringSocialAuthenticator(
#Value("#{properties['facebook.clientId']}")
String clientId,
#Value("#{properties['facebook.clientSecret']}")
String clientSecret,
#Value("#{properties['application.host']}")
String applicationHost) {
this.applicationHost = applicationHost;
facebookConnectionFactory =
new FacebookConnectionFactory(clientId, clientSecret);
}
}
after search and reading discussions and docs, I still don't figure out what to do.
the #{} is spring EL support, don't know how to use it, and don't know the difference to ${}
if I change to #Value("${facebook.clientId}"), there will be no exception, then I use debug mode to read the value of clientId, it does not show 237473571343, it shows ${facebook.clientId}, is that working correctly?
#Value("#{properties['facebook.clientId']}")
In your #Value annotation you are using a SpEL expression. In your case it is going to look for a Map or Properties object named properties and try to find a property with the key facebook.clientId.
To make it work you need to add the following
<util:properties id="properties" location="config.properties" />
Although this works I would strongly suggest to use a *PlaceHolderConfigurer instead of using SpEL.First add atag to your configuration, next change your#Value` to simply use properties.
<context:property-placeholder location="config.properties" />
Then your #Value can be like
#Value("${facebook.clientId}")
The added advantage of this is that you could also use system or environment properties to do some configuration (or override parts of your configuration).

Resources