Grails - using Spring el expressions in Spring 3.1's #Cacheable - spring

We're planning to use the Spring 3.1 cache abstraction instead of the Grails Spring cache plugin. I've experimented with it locally, but an issue occurred when using Spring el expressions like
#Cacheable(value = 'dashboardCache', key = 'sessionStorageService.getUser()', condition = 'sessionStorageService.getUser() != null')
public List<BusinessDashboard> getUserDashboards(String serverName, SessionStorageService sessionStorageService) { ... }
the following error occurs when executing the integration test case
EL1008E:(pos 0): Field or property 'sessionStorageService' cannot be found on object of type 'org.springframework.cache.interceptor.CacheExpressionRootObject'
org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Field or property 'sessionStorageService' cannot be found on object of type 'org.springframework.cache.interceptor.CacheExpressionRootObject'
I assume this behavior is caused by missing debug information - thus my question:
Can Spring el expressions be enabled in Grails apps, or is there any parameter to the compilation process to tell Grails to keep debug symbols in the class files?
(we're running on Grails 2.0.1)

you should try '#sessionStorageService.getUser()'

Related

How to deserialize json object referring related data using hal links with Jackson and GraalVm?

I'm currently struggling to implement a restfull api using
-> Spring Boot 3.0.0, using spring data rest and spring hateoas
-> Spring native support
Basically, I would like to patch an entity, submitting a new value for a field (pointing to an existing entity in Database).
The reference is handled with an hal link. below is the Json object I "PATCH" to my EntityRepository controller:
{
"shouldBeChecked": true,
"name": "Amical6",
"parent": null,
"authority": "https://myurl/api/authorities/e52cdfb6-6f3c-4552-8ea4-e1357b5d052c"
}
Everything is fine when I run the app from my IDE (without GraalVM Native compilation). But when I go live on my test environment (copiled with GraalVM), I get the following errors :
in the web navigator (running the client app):
{"cause":{"cause":null,"message":"Cannot construct instance of `org.[xxx].Authority` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('https://myurl/api/authorities/e52cdfb6-6f3c-4552-8ea4-e1357b5d052c')\n at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: org.[xxx].MyObject[\"authority\"])"},"message":"Could not read payload"}
And, in my application logs :
Failed to evaluate Jackson deserialization for type [[simple type, class org.springframework.data.rest.webmvc.PersistentEntityResource]]: java.lang.NullPointerException
I'm pretty sure I've got to add some Native Hints related to Jackson (or maybe Spring hateoas ?). Could someone help me with this?
Regards,
EDIT 2022.11.30
Please find below a small reproducer project:
https://github.com/mathieupedreropro/spring-hal-jackson-graalvm
TLDR
I tried to use Spring Hateoas link handling to patch data to my Rest Webservice in a Native compiled Spring boot 3.0.0 application.
It fails at runtime, when the same application works like a charm using a traditional JDK

getRequestURI is null with Netty and Spring Boot 3

In Thymeleaf < 3.1 I used below expression to get the request URI.
th:classappend="${#arrays.contains(urls, #httpServletRequest.getRequestURI()) ? 'active' : ''}"
It worked all the time till recently I upgraded to Spring Boot 3.0 that pulls Thymeleaf 3.1. I am getting this exceptions:
[THYMELEAF][parallel-2] Exception processing template "index": Exception evaluating SpringEL expression: "#arrays.contains(urls, #servletServerHttpRequest.getRequestURI()) ? 'active' : ''" (template: "fragments/header" - line 185, col 6)
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method getRequestURI() on null context object
What is the alternative now since I am using Netty instead of Tomcat in Spring Boot 3.0? I could not figure this from here.
As a workaround, for now to tackle this, I am using:
#GetMapping ("/")
String homePage(Model model) {
model.addAttribute("pagename", "home");
return "index";
}
AND
th:classappend="${pagename == 'home' ? 'active' : ''}"
In Thymeleaf 3.0, access is provided to HttpServletRequest:
#request : direct access to the javax.servlet.http.HttpServletRequest object associated with the current request. reference
This has been removed from Thymeleaf in 3.1.0. Here is the equivalent section from the documentation: Web context namespaces for request/session attributes, etc..
The "what's new in 3.1" documentation does not specifically mention HttpServletRequest, but it does mention the removal of all the "web-API based expression utility objects".
The #request, #response, #session, and #servletContext are no longer available to expressions in Thymeleaf 3.1.
Spring Boot 3.0.0 uses Thymeleaf 3.1.0 (as you noted).
What to do instead?
See the related GitHub issue: Recommended way to go after upgrade to SpringBoot3 - attributes
Specifically:
These objects are not directly available in templates in Thymeleaf 3.1 for security reasons. The recommended way to make this information available to templates is to add the specific pieces of information that are really needed by the template as context variables (model attributes in Spring).
Example:
model.addAttribute("servletPath", request.getServletPath();
That is the same basic approach as what you are already doing, in your work-around.
See also: Remove web-API based expression utility objects
Adding to #andrewJames answer,
If you are using request.getServletPath() in many pages, then in such case, it's more convenient to use Spring's #ModelAttribute annotation in a #ControllerAdvice class. It will register this #ModelAttribute method for all controllers in your app. Example:
#ControllerAdvice
public class GlobalController {
#ModelAttribute("servletPath")
String getRequestServletPath(HttpServletRequest request) {
return request.getServletPath();
}
}
Finally in any page you can access by using:
${servletPath}

Spring expression fails after upgrade to SpringBoot 2.2

I had an application which was on SpringBoot 1.5.9. I recently upgraded to SpringBoot 2.2. I had a bunch of issues which I fixed, and now the app starts up without errors.
However, I have a #PreAuthorize on my controller methods which seems to be failing:
#PreAuthorize("not #appConfig.getSecEnabled() or hasRole('ROLE')") public void someMethod() {}
java.lang.IllegalArgumentException: Failed to evaluate expression 'not #appConfig.getSecEnabled() or hasRole('ROLE')'
What has changed in 2.2 that could cause this?

SpringBoot not resolving devtools properties in tests

I'm using spring-boot-devtools with my SpringBoot application (2.2.8) to store secrets outside of my repository. This works for the running application, but integration tests fail with Unexpected exception during bean creation; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'secret.key' in value "${secret.key}".
SecretController.kt
#RestController
class SecretController(
#Value("\${secret.key}") private val secret: String
) {
#GetMapping("/secret")
fun secret(): String {
return secret
}
}
.spring-boot-devtools.properties
secret.key: secret-asdf
In an older application (SpringBoot 2.1) this works fine. I know they changed the path with SpringBoot 2.2, but kept the old path as backwards compatibility - I tried both without success. I also upgraded to 2.3.1, but this does not help either.
Any ideas on how to read properties in integration tests with SpringBoot >= 2.2?
This isn't a bug but a conscious change, see https://github.com/spring-projects/spring-boot/issues/5307
You shouldn't rely on devtools to configure your tests this way.

About map properties to java class in spring boot 2

I want to convert properties to map, see below
field2ZhNameMap.platform=平台
==>
private Map<String,String> field2ZhNameMap;
In Spring boot 1.5.6 start the app in tomcat it's OK, but use sprint boot 2.0.0.M7 start the app in tomcat I got below error
Caused by: org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under '' to com.foo.bar.util.Field2ZhNameProperties
at org.springframework.boot.context.properties.bind.Binder.handleBindError(Binder.java:227)
Caused by: java.lang.IllegalArgumentException: PropertyName must not be empty
at org.springframework.util.Assert.hasLength(Assert.java:233)
at org.springframework.boot.origin.PropertySourceOrigin.<init>(PropertySourceOrigin.java:41)
After debuging source code I found start the app in tomcat it has a JndiPropertySource which caused above problem. So I have to explicitly disable JndiPropertySource by specify spring.jndi.ignore=true in a spring.properties to solve this problem.
In addition I found these classes like Binder do not exist in 1.5.6, it seems it has a big change from 1.5.6 to 2.0.0. So I want to know if has some documents record these change and guide how to correctly map properties to java class in spring boot 2?
For me, upgrade to Spring Boot 2.0.1.RELEASE on Tomcat 8.5.30 resolved PropertyName must not be empty

Resources