I am working on a microservice in Spring Boot, and I have a common configuration class where I store all the config variabes that I got from application.properties file. It looks something like this:
Config.java
package programming
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
#Configuration
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CommonConfiguration {
#Value("${user-pool.id}")
private String userPoolId;
#Value("${client.id}")
private String clientId;
...
...
...
}
Then whenever I need these variables in other classes, I autowire Config.java class, and simply use it like this:
#Autowired
private Config config;
public void method1() {
...
String key = items.get(config.getClientId()).toString();
}
Would having a common configuration class that stores all the autowired variables and use these when needed in other classes a good practice in spring boot? If not, what would be the best way to go about this? Any help would be greatly appreicated. Thank you!
The better way is to do something ging like this
#ConfigurationProperties(prefix = "optional.key")
Public class MyProperties{
Int example;
String example2;
Getters/setters
}
In your application.properties you can now type
optional.key.example=5
You can the Autowerire MyProperties where ever you need them
Edit
It’s also good to add the following dependency to maven, it generates a helper file so that your idea can recognise the defined properties
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
Small addition to replay on the question in the comments
You can leave the prefix empty and just annotate you class with #ConfigurationProperties if you don't want to have a general key
Subkeys can be easily handled with subclasses I will post an example of one of my projects here
#ConfigurationProperties(prefix = "swagger")
public class SpringFoxProperties {
private Info info = new Info();
private Config config = new Config();
public Info getInfo() {
return info;
}
public void setInfo(Info info) {
this.info = info;
}
public Config getConfig(){
return config;
}
public void setConfig(Config config) {
this.config = config;
}
public static class Config {
private String paths = "";
private String documentationType = "openApi";
public String getPaths() {
return paths;
}
public void setPaths(String paths) {
this.paths = paths;
}
public String getDocumentationType() {
return documentationType;
}
public void setDocumentationType(String documentationType) {
this.documentationType = documentationType;
}
}
public static class Info {
private String title = "";
private String description = "";
private String version = "";
private String termsOfServiceUrl = "";
private Contact contact = new Contact();
private License license = new License();
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getTermsOfServiceUrl() {
return termsOfServiceUrl;
}
public void setTermsOfServiceUrl(String termsOfServiceUrl) {
this.termsOfServiceUrl = termsOfServiceUrl;
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public License getLicense() {
return license;
}
public void setLicense(License license) {
this.license = license;
}
public static class Contact {
private String name = "";
private String url = "";
private String email = "";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
public static class License {
private String name = "";
private String url = "";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
}
}
example application.yml
swagger:
info:
title: title
description: description
version: version
termsOfServiceUrl: termsOfServiceUrl
contact:
name: name
url: url
email: email
license:
name: name
url: url
config:
paths: /api/.*
documentation-type: swagger
Would having a common configuration class that stores all the autowired variables and use these when needed in other classes a good practice in spring boot? If not, what would be the best way to go about this? Any help would be greatly appreicated.
My answer to that part is: definitely no!
You are introducing a real class dependency for all classes that need configuration. With that you are actually reversing the configuration injection that spring offers.
The whole point is to be able to inject only that specific part of the configuration you need in fields of your target class directly (e.g. by using #Value("${foo.bar}" private String fooBar;))
With that you also disable use of specific features (e.g. using a different profile in different classes with #Profile), and i'm sure there are more examples that you are actually limiting the flexibility.
I don't see any benefit in your approach - you may aswell explain why you need that :)
Related
Refer to the seven security areas outlined in the Vulnerability Assessment Process Flow Diagram. Use what you’ve learned in steps 1 and 2 to guide your manual review. Identify all vulnerabilities in the Project One Code Base, linked in Supporting Materials, by manually inspecting the code. Document your findings in your vulnerability assessment report. Be sure to include a description that identifies where the vulnerabilities are found (specific class file, if applicable).
Vulnerability Process Flow Diagram
#SpringBootApplication
public class RestServiceApplication {
public static void main(String[] args) {
SpringApplication.run(RestServiceApplication.class, args);
}
}
public class myDateTime {
int mySecond;
int myMinute;
int myHour;
int[] retrieveDateTime() {
/* implement accessor method */
return new int[3];
}
void setMyDateTime(int seconds, int minutes, int hour) {
/* implement accessor method */
}
}
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#GetMapping("/greeting")
public Greeting greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
public class customer {
private int account_number;
int account_balance;
public int showInfo() {
//code to show customer information
return this.account_number;
}
public void deposit(int a) {
account_balance = account_balance + a;
}
}
#RestController
public class CRUDController {
#RequestMapping("/read")
public CRUD CRUD(#RequestParam(value="business_name") String name) {
DocData doc = new DocData();
return new CRUD(doc.toString());
}
}
public class CRUD {
private final String content;
private final String content2;
public CRUD(String content) {
this.content = content;
this.content2 = content;
}
public CRUD(String content1, String content2) {
this.content = content1;
this.content2 = content2;
}
public String getContent() {
return content;
}
public String getContent2() {
return content2;
}
}
Honestly, I am just confused about what I am doing and looking for in the code for it to be classified as a vulnerability. Anything helps, thanks.
I would like to read the array in the YAML file in java spring. I don't know if it's possible to use an array of objects.
Here is the content of the YAML:
main:
nodes:
-
label: "My label"
id: "My label id"
uri: ""
-
label: "My label"
id: "My label id"
uri: ""
And here is my component which loads the file:
public class MyNode {
String label, id, uri;
}
#ConfigurationProperties(prefix = "main")
#PropertySource(value = "classpath:mycustomfile.yml", factory = YamlPropertyLoaderFactory.class)
#Component
public class CustomProperties {
private List<MyNode> nodes;
}
And the YamlPropertySourceFactory
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource)
throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
}
}
When I run my application, nodes are null. I followed Baeldun tutorial
I solved it by adding #Data (Lombok) and make MyNode as inner static class. You can also add getter and setter if you don't use Lombok.
#ConfigurationProperties(prefix = "main")
#PropertySource(value = "classpath:mycustomfile.yml", factory = YamlPropertyLoaderFactory.class)
#Component
public class CustomProperties {
private List<MyNode> nodes;
#Data
public static class MyNode {
private String label, id, uri;
}
}
It seems that the problem comes from the visibility of your MyNode class members.
If you define public setters for all the members, everything will work fine.
So just try to modify
public class MyNode {
String label, id, uri;
}
to
public class MyNode {
private String label, id, uri;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
I implemented this springboot Component that is supposed to return the value of some properties at runtime
#Component
public class ConnectionService {
#Value("${spring.datasource.url}")
private String url;
#Value("${spring.datasource.username}")
private String username;
#Value("${spring.datasource.password}")
private String password;
#Value("{spring.liquibase.change-log}")
private String changelog;
public ConnectionService() {
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getChangelog() {
return changelog;
}
public void setChangelog(String changelog) {
this.changelog = changelog;
}
}
But for some reason all the properties are being returned as null
This is how I call my component
#SpringBootApplication
public class MigrationsApplication {
private static final Logger logger = LoggerFactory.getLogger(MigrationsApplication.class);
#Autowired
static
ConnectionService cs;
public static void main(String[] args) throws DatabaseException {
try{
cs=new ConnectionService();
String dbUrl=cs.getUrl();
logger.info("URL:"+dbUrl);
String dbUsername=cs.getUsername();
logger.info("USERNAME:"+dbUsername);
String dbPassword=cs.getPassword();
logger.info("PASSWORD:"+dbPassword);
String changelogPath=cs.getChangelog();
}
What am I doing wrong? Do I need a full constructor for the Component?It's because cs is static? Wasn't springboot meant to fill the values automatically?
Component means this is a bean, so Spring will handle its lifecycle. When the bean is created it will fill it with the requested data. However, you try to fill static values with it which is not a part of the lifecycle.
Add a full arg constructor to the class and let Spring inject it to the place where you want to use it and it will work fine.
Edit:
I just realized you not using static variables so the field injection should work when you properly let Spring to initialize your bean
Edit2:
Based on your snippet you using it wrong.
Do not put business logic into the main void, use SpringApplication.run, also, if you want a CLI then use Spring's CommandLineRunner interface like that:
#SpringBootApplication
public class MigrationsApplication.class implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MigrationsApplication.class);
#Autowired
ConnectionService cs;
public static void main(String[] args) {
SpringApplication.run(MigrationsApplication.class, args);
}
#Override
public void run(String... args) {
String dbUrl=cs.getUrl();
logger.info("URL:"+dbUrl);
String dbUsername=cs.getUsername();
logger.info("USERNAME:"+dbUsername);
String dbPassword=cs.getPassword();
logger.info("PASSWORD:"+dbPassword);
String changelogPath=cs.getChangelog();
}
}
I am trying to parse the JSON result from the Wordpress plugins API using Retrofit2 and GSON. I have generated my POJO using the well known website and modified it into the following model:
PluginsApiResponse.java
public class PluginsApiResponse {
#SerializedName("plugins")
#Expose
private List<Plugin> plugins = null;
public List<Plugin> getPlugins() {
return plugins;
}
public void setPlugins(List<Plugin> plugins) {
this.plugins = plugins;
}
}
Plugin.java
public class Plugin {
#SerializedName("name")
#Expose
private String name;
#SerializedName("homepage")
#Expose
private String homepage;
#SerializedName("screenshots")
#Expose
private Screenshots screenshots;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHomepage() {
return homepage;
}
public void setHomepage(String homepage) {
this.homepage = homepage;
}
public Screenshots getScreenshots() {
return screenshots;
}
public void setScreenshots(Screenshots screenshots) {
this.screenshots = screenshots;
}
}
Screenshots.java
public class Screenshots {
#SerializedName("1")
#Expose
private com.dkalsan.retrofitwordpress._1 _1;
#SerializedName("2")
#Expose
private com.dkalsan.retrofitwordpress._2 _2;
#SerializedName("3")
#Expose
private com.dkalsan.retrofitwordpress._3 _3;
public com.dkalsan.retrofitwordpress._1 get1() {
return _1;
}
public void set1(com.dkalsan.retrofitwordpress._1 _1) {
this._1 = _1;
}
public com.dkalsan.retrofitwordpress._2 get2() {
return _2;
}
public void set2(com.dkalsan.retrofitwordpress._2 _2) {
this._2 = _2;
}
public com.dkalsan.retrofitwordpress._3 get3() {
return _3;
}
public void set3(com.dkalsan.retrofitwordpress._3 _3) {
this._3 = _3;
}
}
_1.java (_2.java and _3.java are identical)
public class _1 {
#SerializedName("src")
#Expose
private String src;
#SerializedName("caption")
#Expose
private String caption;
public String getSrc() {
return src;
}
public void setSrc(String src) {
this.src = src;
}
public String getCaption() {
return caption;
}
public void setCaption(String caption) {
this.caption = caption;
}
}
The problem occurs in case the screenshots field contains no entries. I've set up the HttpLoggingInterceptor, which logs the response code 200 and the json in its entirety. I've also excluded the possibility of it being the internet connectivity issue according to the following article. If I remove the screenshots field from the model there is no trouble parsing. Is it possible that the error persists due to GSON trying to deserialize the nonexistent fields 1, 2, and 3 and if so, how to deal with it?
Turn out the problem was in the JSON response formatting. If there were no screenshots it was formatted as a JSON array, otherwise it was formatted as a JSON object containing objects 1, 2, 3, etc. I've managed to fix it by following the answer on this stackoverflow question.
trying to access JSON data from the following:
{"actions":[{"actionType":0,"email":"contact#tonyspizza.com","faIcon":"fa-envelope",
"name":"Contact Us","subject":"Email from Tony's Pizza App"},
{"actionType":2,"faIcon":"fa-phone","name":"Call Us","number":"5204558897"}],
"total":2}
I'm trying to use retrofit to access the 'actions' as each individual classes. (i.e. ActionEmail, ActionPhone, etc). I cannot figure out a way to separate these into separate classes and not have one class with all the properties.
Thanks in advance!
Call<ActionWrapperObject> getActions(// Put your api call body in there);
Here is your ActionWrapperObject
public class ActionWrapperObject {
ArrayList<ActionModel> actions;
public ArrayList<ActionModel> getActions() {
return actions;
}
public void setActions(ArrayList<ActionModel> actions) {
this.actions = actions;
}
}
Here is your ActionModel
public class ActionModel {
int actionType;
String email;
String faIcon;
String name;
String subject;
public int getActionType() {
return actionType;
}
public void setActionType(int actionType) {
this.actionType = actionType;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFaIcon() {
return faIcon;
}
public void setFaIcon(String faIcon) {
this.faIcon = faIcon;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
}
You in your response
Your api call.enqueue(new Callback<ActionWrapperObject>() {
#Override
public void onResponse(Call<ActionWrapperObject> call, Response<ActionWrapperObject> response) {
ActionWrapperObject actionWrapperObj= response.body();
if (actionWrapperObj!= null) {
ArrayList<ActionModel> actionModelList= actionWrapperObj.getActions();
//Here you got the list of actions. Do what ever you want with them. You can
// differentiate each action on its type.
}
}
What I infer is you want to generate fields of the ActionModel class dynamically. You can refer to generating JSON pojo dynamically using reflection.