How to use messageSource bean for processing Hibernate validation messages? - spring

I have this message source bean. It works well for getting messages, e.g. from org.springframework.validation.Validator.
#Bean(name = "messageSource")
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setCacheSeconds(-1);
messageSource.setFallbackToSystemLocale(false);
messageSource.setDefaultEncoding("UTF-8");
messageSource.setBasename("classpath:/locale/messages");
return messageSource;
}
and I would like to use this bean for processing JSR 349 validation messages for such POJO class:
public class AuthorizationRequest {
#NotEmpty
//#NotEmpty(message = "validation.notEmpty")
#JsonProperty("response_type")
private String responseType;
#NotEmpty
//#NotEmpty(message = "validation.notEmpty")
#JsonProperty("client_id")
private String clientId;
#NotEmpty
//#NotEmpty(message = "validation.notEmpty")
#JsonProperty("redirect_uri")
private String redirectUri;
private String scope;
// the rest omitted
}
But error messages from are still (localized) original Hibernate, such {org.hibernate.validator.constraints.NotEmpty.message}. But I would like to use my own error messages. I have tried many options but none of them work.
I would like to remain one message properties file for whole application.
Question
Is there some way how to tell Spring to use my messageSource bean?

Are you sure this is not working(commented lines)? Placing your messages in property file (resources/locale/message.properties) should work...

you need follow this format in Resource Bundle file.
ErrorType.className.fieldName = message.
example:
public class Call{
#Pattern(regexp = "^(http://|https://)?(www.)?([a-zA-Z0-9]+).[a-zA-Z0-9]*.[a-z]{3}.?([a-z]+)?$")
private String site;
}
and just define message in Resource Bundle like this
Pattern.call.site = site address is wrong.

Related

Spring #Value not working in Spring Boot 2.5.5, getting null values

I am trying to inject some property values into variables by means of Spring #Value annotation but I get null values. I tried different configurations and triks but it doesn't work. Think is that before today everythink was working properly. I do not know what I changed in order to get things broken.
Here is my java class:
#Component
#ConditionalOnProperty(prefix = "studioghibli", name = "get")
public class StudioGhibliRestService {
#Value("${studioghibli.basepath}")
private static String BASE_PATH;
#Value("${studioghibli.path}")
private static String PATH;
#Value("${studioghibli.protocol:http}")
private static String PROTOCOL;
#Value("${studioghibli.host}")
private static String HOST;
private static String BASE_URI = PROTOCOL.concat("://").concat(HOST).concat(BASE_PATH).concat(PATH);
#Autowired
StudioGhibliRestConnector connector;
public List<StudioGhibliFilmDTO> findAllFilms() throws SipadContenziosoInternalException {
var response = connector.doGet(BASE_URI, null, null);
if (!response.getStatusCode().is2xxSuccessful() || !response.hasBody()) {
throw new SipadContenziosoInternalException(Errore.INTERNAL_REST_ERROR, "FindAll(), microservizio ".concat(BASE_URI), null);
}
return (List<StudioGhibliFilmDTO>) response.getBody();
}
}
As you can see, the class is annotated with #Component, that because I will need to use it as #Service layer in order to make a rest call in my business logic.
The class is also annotaded with conditional on property...
Here is a screenshot of the debug window at startup:
Since the PROTOCOL value is null, i get a null pointer exception immediately at start up.
Here is part of the application-dev.properties file:
studioghibli.get
studioghibli.protocol=https
studioghibli.host=ghibliapi.herokuapp.com
studioghibli.basepath=/
studioghibli.path=/films
First of all, #Value annotation does not work with static fields.
Secondly, fields with #Value annotation is processed when the instance of the class (a bean) is created by Spring, but static fields exist for a class (for any instance), so when the compiler is trying to define your static BASE_URI field other fields are not defined yet, so you get the NPE on startup.
So you might need a refactoring, try to inject values with the constructor like this:
#Component
#ConditionalOnProperty(prefix = "studioghibli", name = "get")
public class StudioGhibliRestService {
private final StudioGhibliRestConnector connector;
private final String baseUri;
public StudioGhibliRestService(StudioGhibliRestConnector connector,
#Value("${studioghibli.basepath}") String basePath,
#Value("${studioghibli.path}") String path,
#Value("${studioghibli.protocol:http}") String protocol,
#Value("${studioghibli.host}") String host) {
this.connector = connector;
this.baseUri = protocol.concat("://").concat(host).concat(basePath).concat(path);
}
// other code
}
Thanks, It works for me, I have to add some codes to my project. Then I check the spring core document in "#Value" section. Besides
When configuring a PropertySourcesPlaceholderConfigurer using
JavaConfig, the #Bean method must be static.
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}

Spring Autowire configuration in flink

i am trying to use the comination of flink and springboot and im having some problems.
Lets say i am having this flow.
Getting json string that have one field date that contains date string.
using map function and ObjectMapper to parse it into object of LocalDateTime
print
This is simple usecase that will describe my probem.
So, i have Word Class represnting Word that contains LocalDateTime field.
#Data
public class Word {
#JsonDeserialize(using = LocalDateTimeSerde.class)
LocalDateTime date;
}
The LocalDateTimeDeserlization is looking like that(I want to autowire the app configuration):
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
#JsonComponent
public class LocalDateTimeSerde extends JsonDeserializer<LocalDateTime> {
private final AppConf conf;
#Override
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(this.conf.getDateFormatter());
return LocalDateTime.parse(jsonParser.getText(), formatter);
}
}
AppConf.java represneting the configuration of the application is:
#Data
#Configuration
#ConfigurationProperties(value = "app")
public class AppConf {
private String dateFormatter;
}
DemoApplication.java:
final StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironment(1);
String example = "{\"date\":\"2019-01-29 00:00\"}";
var stream = env
.fromElements(example)
.map(x->new ObjectMapper().readValue(x,Word.class))
.returns(Word.class);
stream.print();
env.execute("Demo App");
The exception im getting is :
Caused by: java.lang.IllegalArgumentException: Class com.example.demo.LocalDateTimeSerde has no default (no arg) constructor
The main problem here is that the code of the deserialization is running on the TaskManager and over there springboot doesnt take a part, so it doesn`t inject AppConf into the class.
Adding #NoArgsConstructor will not solve the problem
I think i know why it is hapenning (because flink master serialize the classes to the workers and then springboot doesn`t "ScanComponents" and takes control.
Is there any solution for that? I really want to combine spring with flink also in the worker`s function.
Thanks.
In general, I personally don't think it's a good idea to mix those concepts. The easiest solution is to use AutoWired only on the job manager and use explicit dependency injection when you go into Flink-land.
For example, you could extract the date pattern in the DemoApplication and set it on the ObjectMapper. (Don't forget to initialize ObjectMapper only once in your real code!)
If you really want to use AutoWiring. I guess you need to manually trigger the autowiring on taskmanager. There is a related post specifically for ObjectMapper.

Access Hibernate Validator messages

I'm using Hibernate Validator with Spring Boot. I have a variable in a class that I annotated like below.
Public class User {
#Pattern(regexp=".+#.+\\..+", message="Wrong email!")
private String userEmail;
}
I'm using the validation in a controller.
#PostMapping("/users")
public ResponseEntity addUser(#Valid #RequestBody User user, BindingResult result) {
if(result.hasErrors()) {
log.info("There are errors in the input");
}
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<User>> inputErrors = validator.validate(user);
log.info("Errors: " + inputErrors.toString());
}
Is there an easier why to access the validation messages then through the factory? Something like
result.getMessage();
You can autowire message source to your controller
#Autowired
private MessageSource messageSource;
and get the message
messageSource.getMessage(<your key from the error>, null, locale);
You ned locale to get properly localized text. You need to iterate the errors to get all messages for all found validation errors.

Spring Boot JSR303 message code in annotation getting ignored

In my Spring Boot app I have a backing bean where I am using JSR303 validation. In the annotation, I have specified the message code:
#NotBlank(message = "{firstname.isnull}")
private String firstname;
Then in my message.properties I have specified:
firstname.isnull = Firstname cannot be empty or blank
My JavaConfig for the messageSource is:
#Bean(name = "messageSource")
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
The validation works correctly but instead of seeing the actual string, I get the message code in my jsp page. In looking at the log file, I see an array of codes:
Field error in object 'newAccount' on field 'firstname': rejected value []; codes [NotBlank.newAccount.firstname,NotBlank.firstname,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [newAccount.firstname,firstname]; arguments []; default message [firstname]]; default message [{firstname.isnull}]
If I change the my message code in the message.properties to one of the codes in the array, the string displays correctly in my web form. I didn't even have to change the code in the annotation. This indicates to me the code in the message parameter of the annotation is getting ignored.
I don't want to use the default code. I want to use my own. How can I make this work. Can you please provide a code example.
JSR303 interpolation normally works with ValidationMessages.properties file. However you can configure Spring to change that if you want (I was lazy to do so :)) e.g.
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource" />
</bean>
<mvc:annotation-driven validator="validator" />
According to JSR-303 specification message parameters are expected to be stored in ValidationMessages.properties files. But you can override the place where they are looked for.
So you have 2 options:
Move your messages to the ValidationMessages.properties file
Or override getValidator() method of your WebMvcConfigurerAdapter's descendant (JavaConfig in your case):
#Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}

Hibernate Validator custom messages key with class name and field name

I've been trying to add custom messages for validation errors for a REST Service managed by Spring MVC within a #Controller class.
The Employee class:
public class Employee {
#NotNull
#NotEmpty
private String company;
...
}
My REST Service:
#ResponseStatus(value = HttpStatus.CREATED)
#RequestMapping(method = RequestMethod.POST)
public void add(#RequestBody #Valid Employee employee) {
employees.add(employee);
}
And the validation errors parses
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
public #ResponseBody
List<String> validationExceptions(MethodArgumentNotValidException e) {
List<String> errors = new ArrayList<String>();
for (FieldError error : e.getBindingResult().getFieldErrors()) {
errors.add(error.getDefaultMessage());
}
return errors;
}
So I've put a ValidationMessages.properties on the root of my classpath, and I'm not able to get my custom messages with the following key NotEmpty.employee.company.
I know there are many ways to do this with a ResourceBundle and error.getCode(), or even with the key org.hibernate.validator.constraints.NotEmpty.message, but I'd like have specific messages to specific field of specific objects.
I also don't want to do this with #NotEmpty(message = "NotEmpty.employee.company}"). I want it the simplest.
What should I do?
Have you tried to implement your own
org.springframework.validation.MessageCodesResolver
and then declaring your implementation in the config file:
<mvc:annotation-driven message-codes-resolver="org.example.YourMessageCodesResolverImpl"/>
I'd give it a try, it seems this one is able to build custom error codes like the ones you want:
String[] resolveMessageCodes(String errorCode, String objectName, String field, Class<?> fieldType)
The only and important thing I'm not sure is whether it'll override the error codes generated by the hibernate validators...
I hope it helps (and works).
Cheers,
Chico.

Resources