Spring Cacheable how to pass method invocation result to key? - spring

Consider following snippet, I am trying to call a method of propertiesContainer which would be used as a key.
#Cacheable(value = EhCacheManagerApi.CACHE_X_TOKEN, key = ("#{propertiesContainer.getId()}"))
public String getToken(PropertiesContainer propertiesContainer)
I cannot seem to figure out the correct spel expression for key, current format gives me:
org.springframework.expression.spel.SpelParseException: EL1043E:(pos 1): Unexpected token. Expected 'identifier' but was 'lcurly({)'
Before I tried key = ("#propertiesContainer.id") and key = ("#propertiesContainer.getId()")
propertiesContainer is an interface which has method getId returning String.
So presumably this is not the same as bean method invocation with SpEL?

Could you please try this
#Cacheable(value = EhCacheManagerApi.CACHE_X_TOKEN, key = "#{T(java.lang.String).format('%d-%d', #propertiesContainer.id)}")

Did you try the pure expression without the parenthesis:
#Cacheable(value = EhCacheManagerApi.CACHE_X_TOKEN, key="propertiesContainer.id")
This works for me in Spring 4.3.3 where PropertiesContainer is an interface with a getId() method.
Also, you might need to use #p0.id instead of the method parameter name if you don't have debug info in your compiled code. See the accepted answer here but that would give you a different error I suspect.

Related

Is including parameter in params redundant, when also using #RequestPram?

In a example like:
#GetMapping(value = "/artists", params = "genre")
public List<Artist> getArtists(#RequestParam String genre) {
}
is including genre in the params redundant since it is also declared using #RequestParam in the method signature ?
When trying to map to different methods for the same URL, is the method signature the one that metters, or is also defining params necessary?
In the #RequestMapping annotation (and other HTTP method specific variants), the params element is meant for narrowing the request mappings based on query parameter conditions. From the documentation:
The parameters of the mapped request, narrowing the primary mapping.
Same format for any environment: a sequence of myParam=myValue style expressions, with a request only mapped if each such parameter is found to have the given value. Expressions can be negated by using the != operator, as in myParam!=myValue. myParam style expressions are also supported, with such parameters having to be present in the request (allowed to have any value). Finally, !myParam style expressions indicate that the specified parameter is not supposed to be present in the request.
In the other hand, the #RequestParam annotation allows you to bind a query parameter to a method argument.
Refer to the documentation for details.

Spring AOP get method parameter value based on parameter name

Is it possible to get the method parameter value based on parameter name in Spring AOP.
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
Method method = signature.getMethod();
method.getParameters().getName()
// possible to get the paramater names
This approach will get parameter names, not value.
proceedingJoinPoint.getArgs()
will return values not names
Then is it possible to get the value based on a parameter name?
As I searched everywhere does not exist a function that gives parameter value by name and I wrote a simple method that makes this work.
public Object getParameterByName(ProceedingJoinPoint proceedingJoinPoint, String parameterName) {
MethodSignature methodSig = (MethodSignature) proceedingJoinPoint.getSignature();
Object[] args = proceedingJoinPoint.getArgs();
String[] parametersName = methodSig.getParameterNames();
int idx = Arrays.asList(parametersName).indexOf(parameterName);
if(args.length > idx) { // parameter exist
return args[idx];
} // otherwise your parameter does not exist by given name
return null;
}
I searched for the same thing when I had to use AOP for logging function arguments and their values but it seems there is no direct way to get value based on argument name.
What I noticed however us that value returned by method.getParameters().getName() and proceedingJoinPoint.getArgs() was always in sync., i.e., for function
public void foo(String a, String b)
called as
foo("hello", "world");
method.getParameters().getName() returned ["a", "b"] and proceedingJoinPoint.getArgs() returned ["hello", "world"], in order. So you can iterate over the array by index and for each index i, the i'th argument name would correspond to i'th argument value.
I couldn't find a supporting documentation for this behavior but hey, this code has been running on production servers for about an year it never has produced incorrect result. Though I'd be glad if someone can link to a documentation of this behavior. You may even dig into reflectiion's code to verify this behavior.

Spring MVC - Throws exception when the int value of ModelAttribute is null

I'm building an web application using Spring 3.0 MVC.
I have a method which has prototype below.
#RequestMapping(value = "/blahblah/blah.do", method=RequestMethod.GET)
public void searchData(#RequestParam(value="uniqOid", required=false) String uniqOid, #ModelAttribute("MasterVo") MasterVo searchVo,
ModelMap model, HttpServletResponse response, HttpServletRequest request)
The problem is that, the view (jsp) contains inputs that matches to searchVo(ModelAttribute).
When the int or long value of searchVo didn't come from the jsp, the server throws 404 page not found exception.
If the type of value is "String", it has no problem.
In my opinion, it is the problem of type casting.
How could I solve this problem, and which part of the server code that I have to check?
Thanks in advance.
I will go ahead and assume a few things about your problem.
It is not a type-cast problem. Spring has default converters that can easily convert from a String to some primitive type.
Now what you are facing is I think a null assigment to primitive type problem. Suppose the name of the property that's causing the problem is named primitiveProperty. Now, the request-paramters could include a parameter named primitiveProperty with an empty-String value, or some value that cannot be converted to a number. If the type of the primitiveProperty is String, it can assign the value of that parameter to it without any problem.
If the type of the primitiveProperty is int, long or some other primitive type that cannot have a null value, a problem occurs. When Spring converts the empty-string or a non-numeric string valued request-param named primitiveProperty, it cannot do so since that string can't be converted to a valid int or long value. So it is converted to null. Now, when Spring tries to assign that null value to a property that cannot have a null value (any primitve type), you get an Exception. If you are getting an empty-string as your request-param, you can replace the troublesome property in your domain object with its equivalent wrapper class (int with Integer, long with Long and so on). If you are getting a non-numeric value from your view, well, make sure that you don't get a non-numeric value.
You need to check the setter of the fields that are giving the typecast problem, in your case MasterVo .
The Spring will call the setter of the property to bind the value, where i presume you will see the error coming.
Just add a debug point to this setter and you will see the problem.

Where does Grail's errors property come from?

Grails has a bug with regards to databinding in that it throws a cast exception when you're dealing with bad numerical input. JIRA: http://jira.grails.org/browse/GRAILS-6766
To fix this I've written the following code to manually handle the numerical input on the POGO class Foo located in src/groovy
void setPrice(String priceStr)
{
this.priceString = priceStr
// Remove $ and ,
priceStr = priceStr.trim().replaceAll(java.util.regex.Matcher.quoteReplacement('$'),'').replaceAll(',','')
if (!priceStr.isDouble()) {
errors.reject(
'trade.price.invalidformat',
[priceString] as Object[],
'Price:[{0}] is an invalid price.')
errors.rejectValue(
'price',
'trade.price.invalidformat')
} else {
this.price = priceStr.toDouble();
}
}
The following throws a null reference exception on the errors.reject() line.
foo.price = "asdf" // throws null reference on errors.reject()
foo.validate()
However, I can say:
foo.validate()
foo.price = "asdf" // no Null exception
foo.hasErrors() // false
foo.validate()
foo.hasErrors() // true
Where does errors come from when validate() is called?
Is there a way to add the errors property without calling validate() first?
I can't exactly tell you why, but you need to call getErrors() explicitly instead of accessing it as errors like a property. For some reason, Groovy isn't calling the method for it. So change the reject lines in setPrice() to
getErrors().reject(
'trade.price.invalidformat',
[priceString] as Object[],
'Price:[{0}] is an invalid price.')
getErrors().rejectValue(
'price',
'trade.price.invalidformat')
That is the easiest way to make sure the Errors object exists in your method. You can check out the code that adds the validation related methods to your domain class.
The AST transformation handling #Validateable augments the class with, among other things
a field named errors
public methods getErrors, setErrors, clearErrors and hasErrors
The getErrors method lazily sets the errors field if it hasn't yet been set. So it looks like what's happening is that accesses to errors within the same class are treated as field accesses rather than Java Bean property accesses, and bypassing the lazy initialization.
So the fix appears to be to use getErrors() instead of just errors.
The errors are add to your validateable classes (domain classes and classes that have the annotation #Validateable) dinamically.
Allowing the developer to set a String instead of a number doesn't seem a good way to go. Also, your validation will work only for that particular class.
I think that a better approach is to register a custom property editor for numbers. Here's a example with dates, that enable the transform of String (comming from the form) to Date with a format like dd/MM/yyyy. The idea is the same, as you will enforce that your number is parseable (eg. Integer.parseInt() will throw exception).
In your domain class, use the numeric type instead of String, so by code developers will not be allowed to store not number values.

JSTL Expression Language accessing object properties

I was following a tutorial today that had me scratching my head for an hour. Consider:
public class MyClass {
public int getTotal() {
amount = 100;
return amount;
}
}
and an excerpt from a JSP:
<p>Total: ${objectOfTypeMyClass.total}</p> //object instantiated elsewhere
Nowhere in the code was an instance variable named "total" ever declared or used. The only reference to the word "total" in the whole project (other than in the JSP) was the method getTotal().
So after some desperate last-ditch experimentation, it appears that Expression Language evaluates ${someObject.var} as "call the getVar() method of the someObject object.
I worked with this long tutorial for over a week thinking that ${someObject.var} was saying "directly fetch the saved instance variable "var" from someObject.
Did I have it wrong the whole time and is my observation correct that in order to reference any instance variable using EL, you have to provide a corresponding getter method named getVarname() where "Varname" is the name of the instance variable?
Also, EL seems to be case-insensitive in this regard. In my example above, "total" in ${objectOfTypeMyClass.total} is all lowercase where the method getTotal() has a capital "T".
And while we're at it, why don't we need to instantiate the variable "total"? I guess EL isn't actually referencing an instance variable...just a getter method?
What gives?
Did I have it wrong the whole time and is my observation correct that in order to reference any instance variable using EL, you have to provide a corresponding getter method named getVarname() where "Varname" is the name of the instance variable?
That's correct. EL adheres the JavaBeans specification as described in the EL specification.
Also, EL seems to be case-insensitive in this regard. In my example above, "total" in ${objectOfTypeMyClass.total} is all lowercase where the method getTotal() has a capital "T".
No, it's certainly not case insensitive. It's specified behaviour. ${bean.Total} would not have worked.
And while we're at it, why don't we need to instantiate the variable "total"? I guess EL isn't actually referencing an instance variable...just a getter method?
It's because it's supposed to adhere the Javabean specification.
All with all, read the both specifications and everything will be clear :)
See also:
What are the advantages of Javabeans?
The . in objectOfTypeMyClass.total is the JSTL EL Dot Operator. It can do a few different things. Including:
map.key accessed a value from map stored under key. or
object.property accesses property from object using "JavaBeans" conventions.
This should work:
public class MyClass {
private int total = 100;
public int getTotal() {
return total;
}
...
}

Resources