map of map as Bean injection in springboot configuration? - java

i usually use springboot to inject Beans and use patterns like
in a application.properties file
platform.accounts.acbot71.name=acbot71
platform.accounts.acbot71.secret=secret1
platform.accounts.acbot72.name=acbot72
platform.accounts.acbot72.secret=secret2
in a springboot configuration file
#ConfigurationProperties(prefix = "platform.accounts")
#Bean
public Map<String, PlatformAccount> platformAccounts() {
return new TreeMap<>();
}
with PlatformAccount as a pojo with classic getters and setters
String name;
String email;
All is ok. But now, i'd like to do an advanced configuration with 'inner' map
for each value of the existing map.
I'd like to build an application properties like..
platform.accounts.acbot71.name=acbot71
platform.accounts.acbot71.secret=secret1
platform.accounts.acbot71.subaccount1.name=sub1
platform.accounts.acbot71.subaccount1.secret=secret1
platform.accounts.acbot71.subaccount2.name=sub2
platform.accounts.acbot71.subaccount2.secret=secret2
platform.accounts.acbot72.name=acbot72
platform.accounts.acbot72.secret=secret2
platform.accounts.acbot72.subaccount1.name=sub1
platform.accounts.acbot72.subaccount1.secret=secret1
Purpose is to inject a second map into each object of type PlatformAccount
(i.e. the value of the first map)
How can i do that ?
any code as example ?

I'd do it in application.yml instead, and tweak the properties to have a subAccounts map.
platform:
accounts:
acbot71:
name: acbot71
secret: secret1
subAccounts:
subaccount1:
name: sub1
secret: secret1
subaccount2:
name: sub2
secret: secret2
acbot72:
name: acbot72
secret: secret2
subAccounts:
subaccount1:
name: sub1
secret: secret1
I guess it would look like this in application.properties...
platform.accounts.acbot71.name=acbot71
platform.accounts.acbot71.secret=secret1
platform.accounts.acbot71.subAccounts.subaccount1.name=sub1
platform.accounts.acbot71.subAccounts.subaccount1.secret=secret1
platform.accounts.acbot71.subAccounts.subaccount2.name=sub2
platform.accounts.acbot71.subAccounts.subaccount2.secret=secret2
platform.accounts.acbot72.name=acbot72
platform.accounts.acbot72.secret=secret2
platform.accounts.acbot72.subAccounts.subaccount1.name=sub1
platform.accounts.acbot72.subAccounts.subaccount1.secret=secret1
And your PlatformAccount class could look like this.
public class PlatformAccount {
private String name;
private String secret;
private Map<String, PlatformAccount> subAccounts;
}
I've never tried anything like this, Spring might freak out about the nested identical class. If it does, you would have to make a SubAccount config class.

Related

How to convert a Properties Object to Custom Object using Jackson?

In a Spring Boot application, Spring Boot is used to build a Properties object from a YAML file as follows:
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
yamlFactory.setResources(new DefaultResourceLoader().getResource("application.yml"));
Properties properties = yamlFactory.getObject();
The reason why Spring Boot's own parser is used is that it not only reads YAML-compliant settings, but also dot-notated properties like e.g:
artist.elvis.name: "Elvis"
artist.elvis.message: "Aloha from Hawaii"
Now that the Properties object is built, I want to map it into an object like the following for example:
#JsonIgnoreProperties(ignoreUnknown = true)
private record Artist(Elvis elvis) {
private record Elvis(String name, String message) { }
}
My question is:
How can this be done with Jackson? Or is there another/better solution for this?
Many thanks for any help
I saw functionality like that in Ratpack framework.
e.g.:
var propsFileUrl =
Thread.currentThread()
.getContextClassLoader()
.getResource("application.properties");
ApplicationProperties applicationProperties =
ConfigData.builder()
.props(propsFileUrl)
.build()
.get(ApplicationProperties.class);
under the hood it is indeed done by using jackson's object mapper, but the logic is not as trivial to post it here.
here's the library:
https://mvnrepository.com/artifact/io.ratpack/ratpack-core/2.0.0-rc-1
application.yml is the default yml file, so no custom configuration is required. Value annotation should be able to read the properties.
#Value("${artist.elvis.name}")
private String name;
Next part I am not sure about your requirements, but hope this is what you are looking for.
To bind to this object 'constructor' can be a good option.
Class for elvis
#Bean
public class Elvis {
private String name;
private String message;
public Elvis(#Value("${artist.elvis.name"}) final String name, #Value("${artist.elvis.message"}) final String message) {
this.name=name;
this.message=message
}
// getter setter for name and message
}
Now Autowire the created bean to Artist bean
#Bean("artists")
public class Artists {
#Autowired
private Elvis elvis
pubic Elvis getElvis() {
return elvis;
}
}

Spring boot : Field names from properties file, and field values at runtime

I've a scenario to solve, where I need to maintain some key-value pairs in the code. The key names are to be read from properties file. Which I'm going to do like this in spring boot.
#Data
#ConfigurationProperties( prefix = "project.keysList" )
public class KeyNames {
private String key1Name;
private String key2Name;
...
}
I'll get the corresponding values during the run-time. How can I store the values elegantly ? These are the options I'm considering.
Define one more model object called KeyValues and store them.
Define a hashmap called KeyValues and store the keyNames and corresponding values.
Is there a better way other than these two, where I don't need to define a new object for storing the values, which I get during the run time.?
Appreciate any help in this..
Pls ask for clarifications in case my qn is not too clear.
Define your key and value in resources folder of application.properties file and define #Value annotation in the above your Configuration class of field name.
For example:
application.properties file
key1.name=valuename
key2.name=valuename
============================
#ConfigurationProperties( prefix = "project.keysList" )
public class KeyNames {
#Value("${key1.name}")
private String key1Name;
#Value("${key2.name}")
private String key2Name;
}
#Data
#ConfigurationProperties(prefix = "custom")
public class CustomProperties {
private Long id;
private String name;
private String phone;
}

Read application variable (assoc array) form application.yml / java, spring

I am working on a spring project where I need to read multiple account credentials (login and password) from application yml file.
I have written the credentials as an associative array like this (I don't know any better way to do it):
app:
mail:
accounts:
- login: firstlogin
password: firstpassword
- login: secondlogin
password: secondpassword
Then I mapped these values to spring application with #Value annotation:
#Service
public class MyClass {
#Values("${app.mail.accounts}")
private List<Map<String, String>> accounts;
...
}
But spring keep throwing an Exception because it fails to read these values.
Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException:
Could not resolve placeholder 'intelaw.mail.accounts' in value "${app.mail.accounts}"
Without changing your application.yml, you can tune your datastructures and make this work.
Create a class Account
class Account {
private String login;
private String password;
//constructors, getters and setters here
}
and read it using a class annotated with #ConfigurationProperties
#Component
#ConfigurationProperties("app.mail")
class MyClass {
private List<Account> accounts = new ArrayList<>();
//getters and setters here
}
and in your service class, you can use it like :
#Autowired
private MyClass myClass;
void someMethod() {
myClass.getAccounts();
}

Reading from yaml is showing null values

To read from yaml file i am doing the following
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties("partners")
public class YAMLConfig {
private Map<String,String> partners = new HashMap<>();
public void setPartners(Map<String, String> partners) {
this.partners = partners;
}
public Map<String, String> getPartners() {
return partners;
}
}
yaml file
person2:
name: bbb
addresses:
to: jiang
bit: su
partners:
p1: wallet
p2: wallet
p3: wallet
And in other Java file i am Autowiring to get YamlConfig above
#Service
public class ObjectModificationService {
#Autowired
YAMLConfig yamlConfig;
public JSONObject modify(JSONObject jsonObject ) {
String type = yamlConfig.getPartners().get(partnerName.toLowerCase());
}
}
I am not getting any type above , also checked yamlConfig.getPartners() in debug it is coming as null. I was following this tutorial: https://www.baeldung.com/spring-yaml, but then again IDE is showing error when i put ConfigurationProperties without ConfigurationProperties("partners").
In your yml configuration class, you have the properties annotation set to "partners", this means that the YAMLConfig class will attempt to read the partners section only. The "person2" section will not be read.
To retrieve the dependency injected values, simply call: yamlConfig.getPartners()
Also, be sure that the application.yml is in the resources directory and/or in the same directory as the .jar
you have specified Partners map accepts String values. so, try Map value formatted with quotes ('') in yaml config file
person2:
name: bbb
addresses:
to: jiang
bit: su
partners:
p1: 'wallet'
p2: 'wallet'
p3: 'wallet'

Getting subset of application.properties by prefix

I have (partly) this application.properties in my Spring Boot app:
spring.main.banner-mode = off
app.set.a = 100
app.set.b = abc
app.set.c
# ...
I want to get injected a Properties object with all keys/values with prefix "app.". Something directly like this:
#Value(value="${app.*}")
private Properties appProperties; // this obviously won´t work
I have not tried with Properties. But this is how you do with a Map.
application.yml:
test:
my-map:
key1: value1
key2: value2
Java:
#Service
#ConfigurationProperties(prefix="test")
public class MyService {
private Map<String, String> myMap = new HashMap<>(); // add getter
}

Categories

Resources