How to resolve a message with named parameters from Spring's MessageSource - java

I have resource bundle with one of the key/value pairs looking like:
my.key=A message specifying min: {min} and max: {max} parameters
It all works well when used with #Length annotation, Thymeleaf and validators. If error conditions are met the message gets resolved and displayed, however if I try to resolve the same message using the MessageSource.getMessage(...) method there's now way I can get this to work.
I tried the following;
messageSource.getMessage("my.key", new Object[] {Map.of("min", 4, "max", 16)}, validLocale);
also
messageSource.getMessage("my.key", new Object[]{"{min:4}", "max:16"}, validLocale);
And a few more things but every time I get IllegalArgumentException caused by NumberFormatException with the message: "can't parse argument number: min"
Will appreciate any suggestions

Your code should work, but you have to modify it slightly
messageSource.getMessage("my.key", new Object[] { 4, 16 }, validLocale);
And change your key to something like
my.key=A message specifying min: {0} and max: {1} parameters
MessageSource doesn't support named arguments (e.g. {min}). min and max in #Length are just attributes -- they have nothing to with the message.properties' placeholders.

#dsp_user
I know very well that if modified as you proposed it the code would work, but the following code has to work too:
#Length(max = 4, min = 16, message = "{my.key}")
private String username;
and it would cease to work if changed to what you suggested.

Related

spring integration spel expression to extract fields from message payload

Not able to get my head over SPEL for Message payloads. I want to extract data from certain fields of my message payload which is essentially the following JSON converted to Map<String, Object> and passed to a #Transformer
{
"expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations",
"id":"14730",
"self":"https://jira.foo.com/rest/api/2/issue/14730",
"key":"SDP-145",
"fields":{
"issuetype":{
"self":"https://jira.foo.com/rest/api/2/issuetype/10200",
"id":"10200",
"description":"gh.issue.epic.desc",
"iconUrl":"https://jira.foo.com/ghanghor/viewkaka?size=xsmall&kakaId=10501&kakaType=issuetype",
"name":"Epic",
"subtask":false,
"kakaId":10501
},
"priority":{
"self":"https://jira.foo.com/rest/api/2/priority/3",
"iconUrl":"https://jira.foo.com/images/icons/priorities/major.svg",
"name":"Major",
"id":"3"
},
"labels":[
"Lizzy",
"kanban",
"rughani"
],
"updated":"2021-01-21T10:33:38.000+0000",
"status":{
"self":"https://jira.foo.com/rest/api/2/status/1",
"description":"The issue is open and ready for the assignee to start work on it.",
"iconUrl":"https://jira.foo.com/images/icons/statuses/open.png",
"name":"Open",
"id":"1",
"statusCategory":{
"self":"https://jira.foo.com/rest/api/2/statuscategory/2",
"id":2,
"key":"new",
"colorName":"blue-gray",
"name":"To Do"
}
},
"summary":"new epic for Tazzy",
"creator":{
"self":"https://jira.foo.com/rest/api/2/user?username=skadmin",
"name":"skadmin",
"key":"skadmin",
"emailAddress":"Lizzy.t#foo.com",
"displayName":"Lizzy Rughani",
"active":true,
"timeZone":"Asia/Kolkata"
},
"subtasks":[
],
}
}
I'm interested in three nested values here which I'm trying to fetch via following expressions
issueDataMap = {LinkedHashMap#4867} size = 3
"name" -> "#payload['fields']['summary']"
"description" -> "#payload['description']"
"text3" -> "#payload['key']"
I get this error when the expression is applied
org.springframework.expression.spel.SpelEvaluationException: EL1012E: Cannot index into a null value
Here's how I get the payload in the as argument to my transformer
#Transformer
public Map<String, Object> generateCardData(Map<String, Object> payload,
#Header("X-UPSTREAM-WEBHOOK-SOURCE") String projectId) {
followed by
StandardEvaluationContext evaluationContext = evaluationContextFactory.getObject();
and here's how I evaluate it
new SpelExpressionParser().parseExpression(issueDataMap.get(key)).getValue(
evaluationContext, payload, String.class)));
I have the app annotated with #SpringBootApplication and #EnableInegrationand I autowire an instance of IntegrationEvaluationContextFactoryBean to get the StandardEvaluationContext
I also tried the variant
issueDataMap = {LinkedHashMap#4867} size = 3
"name" -> "payload['fields']['summary']"
"description" -> "payload['description']"
"text3" -> "payload['key']"
but then I get
EL1008E: Property or field 'payload' cannot be found on object of type 'java.util.LinkedHashMap' - maybe not public or not valid?
First of all it is not clear why would one use SpEL in the code manually, when you have full access to the object. Plus you should keep in mind that create StandardEvaluationContext, parse an expression and evaluate it on every single call is kinda an overhead by performance. You probably just need to change your generateCardData() signature to accept a result of the expression instead of the whole map. See #Payload.expression attribute.
Anyway this is not what you would like to hear for your problem. And it is here:
getValue(evaluationContext, payload, String.class))). The root evaluation object is your payload - a Map. So, what you just need to assume in your expression definition that you get access to that root object. Therefore expressions must be like this: fields.summary, description, key.
You typically see in the docs and samples a payload (or header) as a first token in the expression. That is just because Spring Integration uses a Message as a root object for expressions to evaluate.
Now in regards to performance. Even if your logic to select an expression by some key at runtime (issueDataMap.get(key)), you still could parse it only once.

Message parameters not resolved using Thymeleaf

I'm working on a web application using Spring Boot + Thymeleaf and I'm using #Size annotation to validate a field of a form backing object:
#Size(max=50)
private String name;
By using the default error message, all works fine and I'm getting the following message:
size must be between 0 and 50
Now I'm trying to customize the error message. First of all I've tried the following:
#Size(max=50, message="Max size: {max}")
private String name;
And it's working correctly:
Max size: 50
But now I'd like to move the custom message in a messages.properties file. I set the property this way:
Size=Max size: {max}
But now I'm getting this error:
org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringErrorsAttrProcessor' with root cause
java.lang.NumberFormatException: For input string: "max"
It seems the max parameter is null, or something similar...
Why? What am I doing wrong?
In your messages.properties set like below.
Size=Max size {1}
Your field will be like below.
#Size(max=50)
private String name;

Get path variable out of HttpMessageNotReadable exception

We have some web services which are being consumed by mobile clients , in which the mobile clients make some request's and we return response to them. Somehow if the client make any invalid request we throw Custom Exceptions .But recently the mobile client has made some request which was out of range for Long variable. The client has different variables for ex ::
{
"accountId":"343"
"Amount":"90909090909090909090"
}
In case of the value for accountId or Amount are made more than 19 digits we get HttpMessageNotReadable exception as the range is out of long value. But from exception i am not able to fetch for which variable the exception has been raised whether for accountId or Amount. From the exception i am getting this information in _path variable but i am not able to fetch it.
And in the path variable i get something like::
[com.Upload["AccountInfo"], com.Info["Account"]]
Does somebody know how to fetch this information.
The following code prints out the field which causes the exception.
InvalidFormatException invalidFormatException = (InvalidFormatException) exception
.getCause();
System.out.println(invalidFormatException.getPath().get(0)
.getFieldName());
#ArunM's answer works as long as the field is in 1st level viz the example given by OP.
But what happens when the field is in nested json? say paymentType has wrong value in the following example?
{
"userType": "CUSTOMER",
"payment": {
"amount": 123456,
"paymentType": "INTERNET_BANKING"
}
}
In the above example, if there's anything wrong with the value of userType, there will be just one element in _path.
But if any value inside the payment is wrong, say for example paymentType, there will be multiple elements in the _path variable. Each element representing the hierarchical attribute.
So for paymentType, _path will have 2 elements as illustrated below:
_path[0].fieldName = "payment"
_path[1].fieldName = "paymentType"
Hence the correct approach is to get the last element in _path as shown below. If needed, we can build the complete path by using all the elements.
InvalidFormatException ifx = (InvalidFormatException) exception.getCause();
System.out.println(ifx.getPath().get(ifx.size() - 1).getFieldName());
This I believe is the right approach to get the invalid attribute name.

ValidateNestedProperties field sorting

Good day,
I have some mandatory field need to validate when running update() method. The validation is working fine. But I fail to sort the sequence.
My code is as follow:
#ValidateNestedProperties({
#Validate(field = "currentPassword", on = { "update" }, required = true, mask = "^([^\\s]+)$"),
#Validate(field = "newPassword", on = { "update" }, required = true, mask = "^([^\\s]+)$"),
#Validate(field = "confirmPassword", on = { "update" }, required = true, mask = "^([^\\s]+)$") })
And the screen in browser will show as follow:
- Confirm Password is a mandatory field
- Current Password is a mandatory field
- New Password is a mandatory field
I guess it display sort by ascending order, but actually I wish to display at my own ways, which is currentPassword, then newPassword, and then confirmPassword.
I tried google on this but I think my question is wrong thus I cant get the correct response.
Kindly advise.
The easiest way to achieve that is to specify the error messages in the desired order in the output:
<stripes:errors field="currentPassword"/>
<stripes:errors field="newPassword"/>
<stripes:errors field="confirmPassword"/>
If there may be additional errors not bound to a field show them like so:
<stripes:errors globalErrorsOnly="true"/>
Sorting is tricky since error messages are kept in a HashMap. I suppose you would have to write your own implementation.
See this great article on the Stripes site: https://stripesframework.atlassian.net/wiki/display/STRIPES/Display+Errors
And the docs: http://stripes.sourceforge.net/docs/current/taglib/index.html

Bean Validation + Resource Bundle ideas?

Here is what i want to be able to do with bean validation in my JPA entities :
Instead of #NotNull, i would like to do #NotNull(message="keyInMyResourceBundleFile")
I would like also to parameterize the resourceBundle, and i dont know the syntax for it, because it seems to me the message attribute contains only a string.
The parameter itself could be i18n-ed. For example, assuming there's a param attribute for the resourcebundle parameters, in English, #NotNull(message="missing.value", params={"credit card"}) String creditCard; It will be displayed something like this : "Missing required value for field credit card. In Indonesia, it'll be something like "Nilai harus di isi untuk Kartu Kredit. In this example, i cant hardcode the "credit card" because in Indonesia, it's "Kartu Kredit"
Displays the error message defined in the resource bundle on the log file or UI. Im not sure the way on how to do it, should i catch the ConstraintViolationException, get the message, and process the resource bundle by my own code ?
Please share your thoughts on this ?
Thank you !
Regarding 1 + 2
#NotNull(message="{keyInMyResourceBundleFile}")
Curly brackets are the indicator of a parameter substitution
Regarding 3
No idea what you are after. There is no params attribute for #NotNull. I guess you would do
#NotNull(message="{missing.credit.card}")
And of if you place it on another property you would call the key {missing.name}
Regarding 4
The ConstraintViolationException contains the set of *ConstraintViolation*s. Each ConstraintViolation contains the interpolated message as well as the message template. If you want to log it, do it ...

Categories

Resources