Custom rules to validate Springboot configuration - java

In my Springboot program, I would like to validate my configuration.
Let's say I have a configuration such as:
#Component
#ConfigurationProperties("jms")
public class JmsConfiguration {
protected String brokerType = "";
protected String host = "";
protected String port = "";
}
I know I can use #Validated annotations for very basic checks, e.g.
#Component
#ConfigurationProperties("jms")
#Validated
public class JmsConfiguration {
protected String brokerType = "";
protected String host = "";
#Positive
protected String port = ""; /// must be strictly positive
}
But how can I make more complex checks? For example, how can I ensure that brokerType is whether ems or amq but nothing else?
Or even more complex, how can I ensure that the port is greater than 1000 if, and only if brokerType is amq (stupid case but it is for the example)
I tried to use #Constraint but I did not succeed (however, I am not sure this is the best way to do what I want...)
Thanks to help
Regards,
Philippe

The brokerType can be validated to be either ems or amq by creating a enum for it.
The Spring #ConfigurationProperties has first class support for enum.
Please see example here
https://www.logicbig.com/tutorials/spring-framework/spring-boot/configuration-properties-vs-value-conversion.html
Relevant example From above link
#ConfigurationProperties("app")
public class MyAppProperties {
private int refreshRate;
private TimeUnit refreshTimeUnit;
private Currency tradeCurrency;
.............
}

Related

How to use Jackson Mixin to map JsonNode into an object?

Given is a JsonNode with several levels of depth. From this node a few properties (not all) are to be used to build an object from a 3rd-party class. The class has a no-arg constructor as well as getter and setter.
With the help of a Mixin I want to achieve a mapping between the deeper nested properties of the JsonNode and the shallow properties of the object. Unfortunately, I could not find a relevant example besides this, but the example is too simplistic and I could not implement it for my requirement (maybe I am just not capable or patient enough to test all possibilities - then I apologize).
Here an excerpt of the JsonNode:
{
"datasource": {
"jdbcUrl": "jdbc:h2:mem:example-db",
"driverClassName": "org.h2.Driver",
"username": "sa",
"password": "let-me-in",
"hikari": {
"maximum-pool-size": 10
}
}
}
And here a simplified notion of the class to map to:
public class HikariConfig {
private String jdbcUrl;
private String driverClassName;
private volatile String username;
private volatile String password;
private volatile int maxPoolSize;
//...
}
As you can see from the maxPoolSize property, this value has to be mapped from nested with property name "maximum-pool-size" to shallow with property name "maxPoolSize". Since no special settings regarding visibility seem to be required, the following code should be sufficient for deserialization:
HikariConfig config = new ObjectMapper()
.addMixIn(HikariConfig.class, HikariConfigMixin.class)
.readerFor(HikariConfig.class)
.readValue(jsonNode);
What I need now is a working Mixin class. If I wanted to map into a own object, then I could simply avoid mixin and use records like this:
#JsonIgnoreProperties(ignoreUnknown = true)
private record ConfigData(Datasource datasource) {
private record Datasource(
String jdbcUrl,
String driverClassName,
String username,
String password,
Hikari hikari) {
private record Hikari(
#JsonProperty("maximum-pool-size") Integer maxPoolSize) { }
}
}
But of course that does not work here.
My question therefore:
Can somebody please tell me how to declare a Mixin that does a proper mapping? Preferably with annotations, perhaps using `#JsonPath` somehow?
Many thanks for any help

Unable to resolve variable from properties file when tried to access as function parameter using #Value annotation

This may be silly question to ask but i'm unable to find any satisfactory solution to my problem. In java we don't have the concept of default variables so i am trying to give default value from properties file to my function parameters/arguments using #Value annotation, but i'm always getting null and i'm unable to figure why is this happening. Please help me to solve the issue or provide me some appropriate link/reference which may solve my issue.
MainApplication.java
#SpringBootApplication
public class Application
{
public static void main(String[] args)
{
ApplicationContext context = SpringApplication.run(NetappApplication.class, args);
Sample sample = context.getBean(Sample.class);
System.out.println(sample.check(null));
}
}
Sample.java
public interface Sample
{
public String check(String message);
}
SampleImpl.java
#Service
#PropertySource("classpath:app.properties")
public class SampleImpl implements Sample
{
#Value("${test}")
String message1;
#Override
public String check(#Value("${test}") String message)
{
return message;
}
}
app.properties
test=anand
But you are passing null to your method...
Perhaps what you want to do is to assign default value to test in case it's not defined in property file:
#Value("${test:default}");
Then, when properties are autowired by Spring if placeholder resolver doesn't get the value from props file, it will use what is after :.
The best use case for this (that I can think of) is when you create Spring configuration.
Let's say you have a configuration class: for DB access. Simply put:
#Configuration
public class DbConfig {
#Value("${url:localhost}")
String dbUrl;
// rest for driver, user, pass etc
public DataSource createDatasource() {
// here you use some DataSourceBuilder to configure connection
}
}
Now, when Spring application starts up, properties' values are resolved, and as I wrote above you can switch between value from property and a default value. But it is done once, when app starts and Spring creates your beans.
If you want to check incoming argument on runtime, simple null check will be enough.
#Value("${test}")
String message1;
#Override
public String check(String message) {
if (message == null) {
return message1;
}
}

How to connect OSGI bundles (Send entity though bundles)?

I have a REST service, which contains three classes in one module(bundle)
User.java -> Entity
UserService.java -> REST service
UserValidation.java -> Special validator for the entity. Server send entity to this validator and get validation result (true or false):
User.java
#XmlRootElement(name = "User")
public class User {
private long id;
private String name;
private String surname;
private String patronymic;
/*Getters and Setters*/
}
UserService.java
public class UserServiceImpl implements UserService {
private UserDAO userDbDao = new UserDatabaseDAO();
#POST
#Path("/users/")
public Response addUser(User user) {
UserValidator userValidator = new UserValidator(user);
if (userValidator.isValid()) {
User newUser = userDbDao.createUser(user);
return Response.ok().type("application/xml").entity(newUser).build();
} else {
return Response.status(Response.Status.BAD_REQUEST).entity(userValidator.getErrorMessage()).build();
}
}
}
UserValidator.java
public class UserValidator {
private static final int MAX_SIZE_NAME = 50;
private static final int MIN_SIZE_NAME = 2;
private User user;
public UserValidator(User user) {
this.user = user;
}
private BadUserResponse badUserResponse = new BadUserResponse();
private boolean isNameValid(String name) {
if (name == null) {
badUserResponse.setNsp("Null in fields name/surname/patronymic");
return false;
}
String tempName = name.trim();
if (tempName.length() < MIN_SIZE_NAME || tempName.length() > MAX_SIZE_NAME) {
badUserResponse.setNsp(String.format("Fields name/surname/patronymic too long or too short (Allowed length from %d to %d)", MIN_SIZE_NAME, MAX_SIZE_NAME));
return false;
}
for (int i = 0; i < tempName.length(); i++) {
if (!Character.isLetter(tempName.charAt(i))) {
badUserResponse.setNsp("Fields name/surname/patronymic contains wrong symbols (Only letters allowed)");
return false;
}
}
return true;
}
public boolean isValid() {
return (isNameValid(user.getName()) &
isNameValid(user.getSurname()) &
isNameValid(user.getPatronymic()));
}
public BadUserResponse getErrorMessage() {
return badUserResponse;
}
BadUserResponse.java
#XmlRootElement(name="baduserresponce")
public class BadUserResponse {
private String nsp;
public String getNsp() {
return nsp;
}
public void setNsp(String nsp) {
this.nsp = nsp;
}
}
But now, I need to split this into separate bundles. Because, as you can see, they uses functionality of each other. For example UserService.java
just used this UserValidator userValidator = new UserValidator(user);
I need to connect these bundles somehow (OSGI Service, ActiveMQ).
In my opinion it works something like this:
UserService bundle get User entity from REST method.
Put all of the User fields (name, surname, patronymic) to ActiveMQ queue (because UserValidator bundle don't know what's is User entity).
UserValidator bundle get User's fiels from queue and validate them.
UserValidator bundle put validation result (true/false) to queue.
UserService bundle get validation result from queue and send User to DAO.
But this is just a concept. Am I wrong?
What's the best way to pass entity though bundles and how should I do this?
You current way of simply initiating the UserValidator via new is technically fine even if they live in different bundles. If your validator is only needed in this place and is simple I would even leave it in the same bundle.
The other options can make sense to decouple your bundles. Using messaging allows you to avoid sync calls. It can also be use to send the data to a remote machine. JMS messaging is quite heavy weight though. You need a broker and depend on the API. In your case you also directly need the result of the validation. So you would simulate a sync call with JMS. So I would rather avoid this.
Using an OSGi service allows you to decouple from the implementation of the service. In this case it makes sense to create an interface for UserValidator. I would also put this interface into a separate bundle. You then need to register the service in the bundle that implements the validator and bind the service in the bundle that uses the validator. OSGi services are very light weight and by default synchronous. So I think they would fit your problem well.
For registering and binding services do not use the OSGi API directly. Instead use declarative services with annotations. They take away most of the complexity in dealing with OSGi services.
Btw. I am not sure how you do REST. I suggest to have a look at the Aries JAX-RS Whiteboard.

Springboot autowired null value

I'm fairly new to Spring & Spring boot, trying to wrap my head around with the concepts.
I have this sample class, this is just a typed up code to show what i'm trying to do. There are no compilation errors. When I start the server, the MQConnection class code gets executed, the mq properties from the appplication.properties are read and printing. But when another class tries to call the send message to MQ, i'm seeing NUllPointerException
#Component
public class MQConnection {
private String username;
private String password;
private String host;
private Connection connection;
#Autowired
public MQConnection(#value("${username}") String username,
#value("${password}") String password, #value("${host}") String host) {
this.username = username;
this.password = password;
this.host = host;
init();
}
public getConnection() {
return connection
}
private init() {
connection = mqconnection;
System.out.println(username, password, host); // I see the value when the server is started
//code to connect to mq
}
}
What am I missing, these autowired & beans is really confusing for me as i'm new to Spring world. Am I using right flow or completely absurd, I don't know
#Component
public class MQSendMessage {
#Autowired
MQConnection mqConnection;
public void sendMessage(String message) {
connection = mqConnection.getConnection(); //NULL value
//send messageCode throws nullpointerexception
}
}
public class AnotherClass {
#Autowired
MQSendMessage messageq;
public doSomething() {
messageq.sendMessage("hello world");
}
}
Any help to fix the connection that throws nullpointer
It looks like AnotherClass is not instantiated by Spring container. If you want to use Spring-annotation like convention then you have to annotate your class with e.g.#Component annotation. Otherwise Spring wont instantiate this object for you.
Useful tip
Try using constructor injection instead of a field injection. Just like in your MQConnection class. You can even mark all your class fields instantiated in the construct with final keyword so you will be sure that these values wont change (if they are immutable of course) during bean life cycle. Then AnotherClass could look like this:
public class AnotherClass {
private final MQSendMessage messageq;
#Autowired
public AnotherClass(MQSendMessage messageq) {
this.messageq = messageq
}
public doSomething() {
messageq.sendMessage("hello world");
}
}
Spring Boot documentation
Also please read carefully Spring Boot documentation on Spring Beans and dependency injection. It is very well written and describes basic concepts in details. It will make your learning much easier and faster.
I hope it helps.

Make a static variable injectable

i am building a http API client that needs to call out to a specific endpoint like so:
public class MyApiClient {
private static final String ENDPOINT ="http://myapi....";
}
Here the endpoint won't change so its constant. However, I want to be able to override this for testing so that I can test against a mock http server for example.
Whats the best way to do this? Is it just to make it an instance variable and provide it with a starting value:
private String endpoint = ="http://myapi....";
public void setEndpoint(String endpoint){
...
}
Well, there are of course many solutions to this and one way of doing it is to use a system property with a default value:
private static final String DEFAULT_ENDPOINT = "http://myapi....";
private static final String ENDPOINT =
System.getProperty("my.endpoint", DEFAULT_ENDPOINT);
This way you get a configurable way of solving your problem. If you need even more flexibility when initializing your static constants you could also use a static initializer:
private static final String ENDPOINT;
static {
// do initialization here but do not throw any exceptions (bad practice)
// you can e.g. read from files etc...
// Then assign your constant...
ENDPOINT =
}
System properties are passed on the command line as -D parameters e.g:
java -Dmy.endpoint=http://...
But in my opinion, an even better approach is to actually inject the value to the class that is using it:
public class ClassThatIsUsingTheConfig {
private final String endpoint;
public ClassThatIsUsingTheConfig(final String endpoint) {
this.endpoint = endpoint;
}
public void someMethod() {
// use endpoint
}
}
And then, make the selection of which endpoint to use in the caller class. From a test case, this will be very easy to mock.
public class MyTest {
#Test
public void testMethod() {
ClassThatIsUsingTheConfig var = new ClassThatIsUsingTheConfig(TEST_ENDPOINT);
var.someMethod();
}
}
public class MyProdClass {
public void prodMethod() {
ClassThatIsUsingTheConfig var = new ClassThatIsUsingTheConfig(PROD_ENDPOINT);
var.someMethod();
}
}
You can read more about dependency injection here.
On a side note, if you are using some kind of framework for managing dependencies such as Spring Framework or CDI it is common to be able to inject properties and constants in various ways (e.g. based on which environment that is currently running). An example, when using Spring Framework you can declare all your constants in a property file and inject the property using annotations:
#Autowired
public ClassWhoIsUsingTheConfig(#Value("my.endoint") final String endpoint) {
this.endpoint = endpoint;
}
The property file for prod could be along the lines of:
my.endpoint=http://prodserver...
wheras the property file for test would look like this:
my.endpoint=http://testserver...
The approach of using a Dependency Injection engine allows for a very flexible way of handling external constants, paths, resources etc and simplifies your life when it comes to testing the code.

Categories