I'm trying to access values from my application.properties file in the Neo4jConnectionFactory class. I am able to access the properties within my Setup beans class. But, I want to be able to access them from Neo4jConnectionFactory. When I try to do so I get null values. Is this possible?
Applications.properties
neo4j.url = bolt://localhost:7687
neo4j.user = neo4j
neo4j.password = neo4j
SetupBeans.java
#Component
public class SetupBeans {
#Bean
public Neo4jClient neo4jClient() {
return new Neo4jConnectionFactory().build();
}
}
Neo4jConnectionFactory.java
#Configuration("neo4j")
public class Neo4jConnectionFactory {
#Value("${neo4j.url}")
private String url;
#Value("${neo4j.user}")
private String user;
#Value("${neo4j.password}")
private String password;
public Neo4jClient build() {
return new Neo4jClient(getUrl(), getUser(), getPassword());
}
}
MyApplication.java
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I think this automatically prepends the prefix neo4j
#Configuration("neo4j")
So your #Value statments should omit the prefix, like this:
#Value("${url}")
private String url;
#Value("${user}")
private String user;
#Value("${password}")
private String password;
Related
I am writing a springboot application that should calculate the grade of a student. You should read the name of the student from command line, and call an API that will return the student ID in JSON. Then I will take the Student ID and call another API that would return the total of the student Marks in JSON. Then I should calculate the grade accordingly. The calculation is as follow: F=<60, D=<65, C=< 75, B=<85, A=<100.
Below is my hypothetical view of how I will write the project, I think I know how to write the code that will function, but I am stuck with what is the best skeleton for the project or design of classes. Below is how I think it should be written.
#Controller
// Rest call to the Student API
public class StudentController {
private static final Logger log = LoggerFactory.getLogger(ClientController.class);
private RestOperations rest;
private Config config; //to read the URL
.....
#Controller
// Rest call to the grades API
public class GradsController
{
private static final Logger log = LoggerFactory.getLogger(ClientController.class);
private RestOperations rest;
private Config config; //to read the URL
.....
Configuration
#ConfigurationProperties(prefix="api.call")
public class Config {
private String URL;
.....
public class Student {
#JsonProperty("Data")
private Elements[] elements;
.......
public class Grads {
#JsonProperty("Subjects")
private String[] subjects;
.......
#Service
public class CalculateGrade {
private String marks;
private HashMap <String, Integer> gradeMap;
CalculateGrade( String marks)
{
this.marks = marks;
this.carName = carName;
fillMap();
}
private void fillMap()
{
gradeMap= new HashMap<>();
gradeMap.put("A",100);
gradeMap.put("B", 85 );
..........
}
public String getGrade()
{
// do calculations;
}
#SpringBootApplication
#ConfigurationPropertiesScan ("Config")
public class MyApplication {
private static final Logger log = LoggerFactory.getLogger(MyApplication.class);
#Autowired StudentController StudentController;
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
For a skeleton, it looks quite sensible. Controllers to translate from Web to Domain and the business logic in domain/application services (CalculateGrade). Looks a lot like the Hexagonal architecture ("Ports&Adapters").
I have a group of properties as follow:
spring.kafka.producer.edwh.bootstrap-servers=localhost:9092
spring.kafka.producer.edwh.properties.enable.idempotence=true
spring.kafka.producer.edwh.retries=10
spring.kafka.producer.edwh.transaction-id-prefix=slv
spring.kafka.producer.edwh.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.producer.edwh.properties.spring.json.add.type.headers=false
... And I want to map in a class like this by using #ConfigurationProperties(prefix = "spring.kafka.producer.edwh"):
#ConfigurationProperties(prefix = "spring.kafka.producer.edwh")
public class EdwhKafkaProducerConfig {
private String bootstrap_servers;
private String properties_enable_idempotence;
private int retries;
private String transaction_id_prefix;
private String value_serializer;
private boolean properties_spring_json_add_type_headers;
}
... How can I do?
The dotted properties denote separate objects. So if you have
mail.additionalHeaders.redelivery=true
mail.additionalHeaders.secure=true
mail.credentials.username=john
mail.credentials.password=password
Then your config class can look like this:
#ConfigurationProperties(prefix = "mail")
public class ConfigProperties {
private AdditionalHeaders additionalHeaders;
private Credentials credentials;
// getters setters
public class AdditionalHeaders {
private boolean redelivery;
private boolean secure;
// getters setters
}
public class Credentials {
private String username;
private String password;
// getters setters
}
}
Have a look here:
https://www.baeldung.com/configuration-properties-in-spring-boot
If you are using Spring Boot, then you need to annotate your main application class with, in your case:
#EnableConfigurationProperties(value = EdwhKafkaProducerConfig.class)
You may also need to define public accessor methods for your configuration properties.
I have two properties file in a folder in file system.
The path of this folder is passed using system properties -D=path/to/properties/folder
ex: java -jar -DpropDir=abc/def app.jar
These are properties files.
Please note that both files have common key username,password.
mysql.properties
url=jdbc:mysql://localhost:3306
username=root
password=pass
vertica.properties
dburl=jdbc:vertica://123.123.123:4321/abcd
username=abcd
password=pass123
Now I want to access all these properties in respective classes.
MySqlProperties.java and VerticaProperties.java like this.
#Component
public class VerticaProperties {
#Value("${dburl}")
private String dburl;
#Value("${username}")
private String username;
#Value("${password}")
private String password;
public String getDbUrl() {
return dburl;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
and similarly MySqlProperties.java
#Component
public class MySqlProperties {
#Value("${url}")
private String url;
#Value("${username}")
private String username;
#Value("${password}")
private String password;
public String getDbUrl() {
return url;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
But as key is same value is overriding for username and password.
How to access mysql.properties in MySqlProperties.java and vertica.properties in VerticaProperties.java classes.
You import external properties using #PropertySource
Given the location is shared as
-Dmysql.properties=file:/path-to-mysql.properties -Dvertica.properties=file:/path-to-vertica.properties
#Component
#PropertySource("${vertica.properties}")
public class VerticaProperties {
.....
}
#Component
#PropertySource("${mysql.properties}")
public class MySqlProperties {
....
}
or Given
-Dmysql.properties=/path-to-mysql.properties -Dvertica.properties=/path-to-vertica.properties
#Component
#PropertySource("file:${vertica.properties}")
public class VerticaProperties {
.....
}
#Component
#PropertySource("file:${mysql.properties}")
public class MySqlProperties {
....
}
Additionally , you may also use prefix with #ConfigurationProperties along with #PropertySource.
The annotation works best when we have hierarchical properties that all have the same prefix, so we mention the prefix too as a part of the annotation.
Add prefix to keys like mysql.url , vertica.url in respective files
#Component
#PropertySource("${vertica.properties}")
#ConfigurationProperties(prefix="vertica")
public class VerticaProperties {
.....
}
#Component
#PropertySource("${mysql.properties}")
#ConfigurationProperties(prefix="mysql")
public class MySqlProperties {
....
}
Spring boot 2.0.0.RELEASE
I have properties class:
#Configuration
#ConfigurationProperties(prefix="person")
public class PersonProperties {
private AddressProperties addressProperties;
public AddressProperties getAddressProperties() {
return addressProperties;
}
public void setAddressProperties(final AddressProperties addressProperties) {
this.addressProperties = addressProperties;
}
public static class AddressProperties {
private String line1;
public String getLine1() {
return line1;
}
public void setLine1(final String line1) {
this.line1 = line1;
}
}
}
And application.yml:
person:
address:
line1: line1OfAddress
It is not binding properly as my AddressProperties object is null.
When a class has the same name as yml properties AddressProperties -> Address it is working well. I tried to add Qualifier or ConfigurationProperties with a prefix address but it is not working. Unfortunately, I cannot find useful information about this case in spring docs.
How to specify a prefix for nested properties?
Property defined in yaml / property file should match with the variables defined in class.
Either change yaml file as
person:
# addressProperties will also work here
address-properties:
line1: line1OfAddress
Or define your bean as
#Configuration
#ConfigurationProperties(prefix = "person")
public class PersonProperties {
// here variable name doesn't matter, it can be addressProperties as well
// setter / getter should match with properties in yaml
// i.e. getAddress() and setAddress()
private AddressProperties address;
public AddressProperties getAddress() {
return address;
}
public void setAddress(AddressProperties address) {
this.address = address;
}
}
If you want to get all properties under address without defining them in separate bean you can define your PersonProperties class as
#Configuration
#ConfigurationProperties(prefix = "person")
public class PersonProperties {
private Map<String, Object> address;
public Map<String, Object> getAddress() {
return address;
}
public void setAddress(Map<String, Object> address) {
this.address = address;
}
}
Here PersonProperties#address will contain {line1=line1OfAddress}
Now All properties under address will be in the Map.
You could simply un-nest the two classes, allowing each to have it's own prefix.
First class:
#Configuration
#ConfigurationProperties(prefix="person")
public class PersonProperties {
private AddressProperties addressProperties;
public AddressProperties getAddressProperties() {
return addressProperties;
}
public void setAddressProperties(final AddressProperties addressProperties) {
this.addressProperties = addressProperties;
}
}
Second class:
#Configuration
#ConfigurationProperties(prefix="person.address")
public class PersonAddressProperties {
private String line1;
public String getLine1() {
return line1;
}
public void setLine1(final String line1) {
this.line1 = line1;
}
}
Edit: As was pointed out in the comments, you'd have to inject both of these classes if one block of code needed to refer to both sets of properties.
I am pretty new to spring so please bear with me and feel free to suggest more reading with right links if you see necessary.I am using a class to define a connection settings for different Rest endpoints I will be calling out to (username port etc.). So I am thinking of storing them to application.properties as follows:
twitter.username="a"
twitter.password="b"
twitter.host="www.twitter.com"
facebook.username="c"
facebok.password="d"
facebook.host="www.facebook.com"
Now I want to define a class that will take all the prefix (such as "twitter" or "facebook") and return me the configuration class based off of the corresponding properties in applicaton.properties.
I was thinking of doing something similar to following:
#Configuration
#PropertySource("classpath:application.properties")
public class RESTConfiguration
{
class RESTServer{
public String username;
public String password;
public String host;
private RESTServer(String username, String password, String host)
{
this.username = username;
this.password = password;
this.host = host;
}
}
#Autowied
private String ednpointName;
#Bean
public RESTServer restServer(Environment env)
{
return new RESTServer(env.getProperty(ednpointName + ".user"),
env.getProperty(ednpointName + ".password"),
env.getProperty(ednpointName+".host"));
}
}
But it clearly won't work since there will be only one Bean and I won't have a way to pass multiple endpointName. Help appreciated!
A better approach would be to use a factory design pattern. Something along the lines of:
#Configuration
#PropertySource("classpath:application.properties")
public class RESTConfiguration
{
#Bean
public RESTServerFactory restServer()
{
return new RESTServerFactory()
}
}
public class RESTServer {
public String username;
public String password;
public String host;
private RESTServer(String username, String password, String host)
{
this.username = username;
this.password = password;
this.host = host;
}
}
public class RESTServerFactory {
#Autowired Environment env;
public RESTServer getRESTServer(String endpointName)
{
return new RESTServer(env.getProperty(ednpointName + ".username"),
env.getProperty(ednpointName + ".password"),
env.getProperty(ednpointName+".host"));
}
}
An example of using this factory:
#Autowired RESTServerFactory factory;
public RESTServer createServer(String endpoint) {
return factory.getRESTServer(endpoint);
}
Two things to do:
Move RestServer outside to its own class, it should not be an inner class of RestConfiguration.
Use scope=DefaultScopes.PROTOTYPE in order to allow creation of several instances of RestServer, one per injection point:
#Bean(scope=DefaultScopes.PROTOTYPE)
public RESTServer restServer(Environment env) { ...