I want to load a yaml file & store it in Config.java.
Here is my yaml file: (Its much bigger. I am giving a simplified version)
---
application:
admin:
jobInterValTime: 1440
customer: lc
system:
mongo:
host: localhost
port: 27017
dbName: LC_Test
collections:
groupsCollection: groups
membershipCollection: memberships
personsCollection: persons
Here is Config.java:
public class Config {
private Application application;
private System system;
//Getter setter
}
Application.java
public class Application {
private Admin admin;
//Getter Setter
}
Admin.java
public class Admin {
private String jobInterValTime;
private String customer;
//Getter Setter
}
System.java
public class System {
private Mongo mongo;
//Getter Setter
}
Mongo.java
public class Mongo {
private String host;
private String port;
private String dbName;
private Map<String, String> collections;
//Getter Setter
}
But the application & system object inside Config.java is coming null.No exception is happening. Can anybody help?
Here is what I have written.
Config config = null;
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
try{
config = objectMapper.readValue(new File("src/test/java/resources/test1.yaml"), Config.class);
//System.out.println(application.getAdmin().getCustomer());
// System.out.println(unidataConfig.getApplication().getAdmin().getCustomer());
} catch(Exception e) {
e.printStackTrace();
}
I don't know the root cause here, code looks good. But one thing you could try is to read contents as Map or JsonNode first, and see how structure looks like. There may well be a mismatch.
I solved the problem. The mistake was very stupid. In one setter method I have written like this: var1 = var1 instead of this.var1 = var1.
Related
I'm trying to set a value of my application.properties, I need to set there the path of a file.
I know I can do this:
#Value("${catalog.path:theValuePath}")
private String absolutePath;
but I got the value from a method, so I was trying something like this
#Value("${catalog.path}")
private String absolutePath=setCatalogPath();
public String setCatalogPath () {
File file = new File("src/test/resources/MyFile.xml");
String absolutePath = file.getAbsolutePath();
return absolutePath;
}
It's not working and I guess is not the ideal way what I'm doing, any ideas? thanks in advance
Please look at below example. You can apply the #Value annotation in class to be auto wired.Make sure to write getters and setters to the absolutePath variable instead of assigning a value through assignment operator.Then use the get method to get the value back for application.
Data class
#Component
public class Data {
#Value("${catalog.path:theValuePath}")
private String absolutePath;
public String getAbsolutePath() {
return absolutePath;
}
public void setAbsolutePath(String absolutePath) {
this.absolutePath = absolutePath;
}
Returning the value through a method
#RestController
#RequestMapping("/")
public class Mycon {
#Autowired
Data data;
#GetMapping
public String hello(ModelMap model) {
return data.getAbsolutePath();
}
}
Application.properties file
catalog.path:theValuePath="src/test/resources/MyFile.xml"
Here is part of my config.yml:
#Authenctication
AuthenticationConfig:
AuthencticationType: LDAP
LDAPConfig:
LDAPUrl: ldap://localhost:389
ConnectionType: simple
LDAPSecurityConfig:
RootDN: cn=manager,dc=maxcrc,dc=com
RootPassword: secret
UserSearchDN: ou=People,dc=maxcrc,dc=com
GroupdSearchDB: ou=Groups,dc=maxcrc,dc=com
I have a class used for parsing:
public class YamlConfiguraiton {
private AuthenticationConfiguration AuthenticationConfig;
public void setAuthenticationConfig(AuthenticationConfiguration AuthenticationConfig) {
this.AuthenticationConfig = AuthenticationConfig;
}
public AuthenticationConfiguration getAuthenticationConfig() {
return this.AuthenticationConfig;
}
}
However, when I run
try(InputStream in = new FileInputStream(new File(ymalPath))) {
yamlConfig = yaml.loadAs(in, YamlConfiguraiton.class);
} catch (IOException e) {
e.printStackTrace();
}
the following error happens:
Exception in thread "main" Cannot create property=AuthenticationConfig for JavaBean=com.ibm.entity.matching.common.bootstrap.YamlConfiguraiton#e7860081
in 'reader', line 2, column 1:
AuthenticationConfig:
^
Unable to find property 'AuthenticationConfig' on class: com.ibm.entity.matching.common.bootstrap.YamlConfiguraiton
in 'reader', line 3, column 4:
AuthencticationType: LDAP
^
at org.yaml.snakeyaml.constructor.Constructor$ConstructMapping.constructJavaBean2ndStep(Constructor.java:270)
at org.yaml.snakeyaml.constructor.Constructor$ConstructMapping.construct(Constructor.java:149)
at org.yaml.snakeyaml.constructor.Constructor$ConstructYamlObject.construct(Constructor.java:309)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:204)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:193)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructDocument(BaseConstructor.java:159)
at org.yaml.snakeyaml.constructor.BaseConstructor.getSingleData(BaseConstructor.java:146)
at org.yaml.snakeyaml.Yaml.loadFromReader(Yaml.java:524)
at org.yaml.snakeyaml.Yaml.loadAs(Yaml.java:518)
at com.ibm.entity.matching.bootstrap.EntityMatching.boot(EntityMatching.java:55)
at com.ibm.entity.matching.bootstrap.EntityMatching.main(EntityMatching.java:35)
Caused by: org.yaml.snakeyaml.error.YAMLException: Unable to find property 'AuthenticationConfig' on class: com.ibm.entity.matching.common.bootstrap.YamlConfiguraiton
at org.yaml.snakeyaml.introspector.PropertyUtils.getProperty(PropertyUtils.java:159)
at org.yaml.snakeyaml.introspector.PropertyUtils.getProperty(PropertyUtils.java:148)
at org.yaml.snakeyaml.constructor.Constructor$ConstructMapping.getProperty(Constructor.java:287)
at org.yaml.snakeyaml.constructor.Constructor$ConstructMapping.constructJavaBean2ndStep(Constructor.java:208)
... 10 more
Why it complains about cannot finding property AuthenticationConfig while AuthenticationConfig is just the name of the instance variable?
UPDATE
After I changed the instance variables from "private" to "public", they were recognized by SnakeYaml, but this is not what we expect for sure. The classes are not recognized as JavaBean.
UPDATE
I found the root cause. It is the naming convention. If you want SnakeYaml to parse your yaml file, camelCase has to be complied with. The name of setter and getter method is also important. Say there is a private instance variable called ldapConfig, then its getter and setter's name has to be getLdapConfig and setLdapConfig, even getLDAPConfig and setLDAPConfig won't work.
The main reason for the error is that you need to define all the attributes present in Yaml file in POJO class (i.e. YamlConfiguraiton).
You can use the below code to skip the undefined properties.
Representer representer = new Representer();
representer.getPropertyUtils().setSkipMissingProperties(true);
Yaml yaml = new Yaml(new Constructor(YamlConfiguraiton.class), representer);
Firstly, rename the attribute names to camelCase in Yaml file.
Refer the below code:-
Code:-
public class YamlReadCustom {
private static String yamlPath = "/authentication.yaml";
public static void main(String[] args) {
Representer representer = new Representer();
representer.getPropertyUtils().setSkipMissingProperties(true);
Yaml yaml = new Yaml(new Constructor(Authentication.class), representer);
try(InputStream in = YamlReadCustom.class.getResourceAsStream (yamlPath)) {
Authentication authentication = yaml.loadAs(in, Authentication.class);
System.out.println(authentication.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Authentication:-
public class Authentication {
private String authenticationConfig;
private String authencticationType;
private LdapConfig ldapConfig;
//getters and setters
}
LdapConfig:-
public class LdapConfig {
private String ldapUrl;
private String connectionType;
private Map<String, Object> ldapSecurityConfig;
//getters and setters
}
authentication.yaml
authenticationConfig:
authencticationType: LDAP
ldapConfig:
ldapUrl: ldap://localhost:389
connectionType: simple
ldapSecurityConfig:
rootDn: cn=manager,dc=maxcrc,dc=com
rootPassword: secret
userSearchDn: ou=People,dc=maxcrc,dc=com
groupdSearchDb: ou=Groups,dc=maxcrc,dc=com
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! =)
What is faster (and better) ? :
Load variables to a special
static object and when a variable
from configuration file is needed
get variale from static object's
field.
Copy configuration
variable to a local field when
creating new object which needs a
configuration variable.
I prefer instance level myself, as long as you aren't doing it excessively (i.e. Don't read the configuration every time you instantiate something).
Static configurations will give you heartache. Especially for testing.
The best solution in my mind is to use a framework like Spring (or Guice) to inject configuration type information into your objects.
I'd say use on-demand caching. Look at the MapMaker from guava-collections. If you doesn't want to add additional dependency then I'd prefer option 1.
I would abstract the configuration with an interface and provide strategies like:
public interface Config {
public String getUrl();
public String getName();
}
public class PropertiesConfig implements Config {
private final String url;
private final String name;
public PropertiesConfig(String filepath) {
Properties props = // read properties from file input stream
this.url = props.getProperty("url", "");
this.name = props.getProperty("name", "");
}
// getters from interface
}
public class SpringConfig {
private final String url;
private final String name;
public SpringConfig(String contextPath) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(contextPath);
this.url = (String) ctx.getBean("url");
this.name = (String) ctx.getBean("name");
}
// getters from interface
}
Etc., you could provide a bunch of strategies obviously.
public class Application {
private final Config config;
public Application(Config config) {
this.config = config;
}
public String doWork() {
return Client.url(config.getUrl()).get();
}
}