thymeleaf-spring icon indicating copy to clipboard operation
thymeleaf-spring copied to clipboard

EvaluationException after an automatic restart of Spring Boot DevTools

Open marcelopbarros opened this issue 2 years ago • 0 comments

Hi guys. In our project I had a problem that started when we updated Thymeleaf to version 3.0.15. I hope I can help improve Thymeleaf showing the details of the problem.

Brief description of the problem:

In our project we have a method that receives an Enum and is called from the view. In the view, we iterate over the enum values to present a select element and then call the function with every enum. Everything works fine at first glance, but when a hot restart occurs (Spring Boot DevTools) the method returns an error.

Steps to reproduce de problem:

  1. Start a new project on https://start.spring.io/ using any version newer than 2.5.10. Chose only the following dependencies:
  • Spring Web
  • Thymeleaf
  • Spring Boot DevTools
  1. Include the file bellow BasicController.java in src/main/java/com/example/demo:
package com.example.demo;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class BasicController {

    @GetMapping
    public String index() {
        return "basic";
    }

    public String getEnumName(BasicEnum myEnum) {
        return myEnum.name();
    }

    public enum BasicEnum {
        IPSUM, LOREM
    }
}

  1. Include the file bellow basic.html in /src/main/resources/templates:
<html>
<body th:with="BasicEnum=${T(com.example.demo.BasicController$BasicEnum)}">
    <h1 th:each="myEnum : ${BasicEnum.values()}"
    th:text="${@basicController.getEnumName(myEnum)}"></h1>
</body>
</html>
  1. Execute the project on IDE or with ./mvnw spring-boot:run.
  2. Access the main page at http://localhost:8080/. Everything works fine.
  3. Make any dummy change on Java file on IDE or execute ./mvnw compile to fire a hot restart using Spring Boot DevTools.
  4. Now access the main page at http://localhost:8080/. You should see the error message below:
There was an unexpected error (type=Internal Server Error, status=500).
An error happened during template parsing (template: "class path resource [templates/basic.html]")
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/basic.html]")
	at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:241)
	at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parseStandalone(AbstractMarkupTemplateParser.java:100)
	at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:666)
	at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098)
	at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072)
	at org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:366)
	at org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:190)
	at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1401)
	at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1145)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1084)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "@basicController.getEnumName(myEnum)" (template: "basic" - line 4, col 5)
	at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393)
	at org.attoparser.MarkupParser.parse(MarkupParser.java:257)
	at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230)
	... 48 more
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "@basicController.getEnumName(myEnum)" (template: "basic" - line 4, col 5)
	at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:292)
	at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166)
	at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66)
	at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
	at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138)
	at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:144)
	at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74)
	at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95)
	at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633)
	at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314)
	at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205)
	at org.thymeleaf.engine.Model.process(Model.java:282)
	at org.thymeleaf.engine.Model.process(Model.java:290)
	at org.thymeleaf.engine.IteratedGatheringModelProcessable.processIterationModel(IteratedGatheringModelProcessable.java:367)
	at org.thymeleaf.engine.IteratedGatheringModelProcessable.process(IteratedGatheringModelProcessable.java:221)
	at org.thymeleaf.engine.ProcessorTemplateHandler.handleCloseElement(ProcessorTemplateHandler.java:1640)
	at org.thymeleaf.engine.TemplateHandlerAdapterMarkupHandler.handleCloseElementEnd(TemplateHandlerAdapterMarkupHandler.java:388)
	at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler$InlineMarkupAdapterPreProcessorHandler.handleCloseElementEnd(InlinedOutputExpressionMarkupHandler.java:322)
	at org.thymeleaf.standard.inline.OutputExpressionInlinePreProcessorHandler.handleCloseElementEnd(OutputExpressionInlinePreProcessorHandler.java:220)
	at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler.handleCloseElementEnd(InlinedOutputExpressionMarkupHandler.java:164)
	at org.attoparser.HtmlElement.handleCloseElementEnd(HtmlElement.java:169)
	at org.attoparser.HtmlMarkupHandler.handleCloseElementEnd(HtmlMarkupHandler.java:412)
	at org.attoparser.MarkupEventProcessorHandler.handleCloseElementEnd(MarkupEventProcessorHandler.java:473)
	at org.attoparser.ParsingEle<thymeleaf.version>3.0.14.RELEASE</thymeleaf.version>mentMarkupUtil.parseCloseElement(ParsingElementMarkupUtil.java:201)
	at org.attoparser.MarkupParser.parseBuffer(MarkupParser.java:725)
	at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:301)
	... 50 more
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method getEnumName(com.example.demo.BasicController$BasicEnum) cannot be found on type com.example.demo.BasicController
	at org.springframework.expression.spel.ast.MethodReference.findAccessorForMethod(MethodReference.java:225)
	at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:135)
	at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:55)
	at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:386)
	at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:92)
	at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:112)
	at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:338)
	at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:265)
	... 75 more
  1. Now stop the project and include the property bellow in pom.xml:
<thymeleaf.version>3.0.14.RELEASE</thymeleaf.version>
  1. Reproduce again the steps 4 to 7, but now, no error will be rendered no screen.

Workarounds:

The way we've been doing is fixing version of Thymeleaf to 3.0.14. But I see that rewriting the method to receive a String and passing the name(), retrieving again on controller with BasicEnum.valueOf would do the job. It seems like the problem happens exclusively with enums.

marcelopbarros avatar Mar 26 '22 00:03 marcelopbarros