Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qute calls accessor method instead of using property for boolean (since 1.12.0.Final) #15334

Closed
derkoe opened this issue Feb 25, 2021 · 7 comments · Fixed by #15340
Closed

Qute calls accessor method instead of using property for boolean (since 1.12.0.Final) #15334

derkoe opened this issue Feb 25, 2021 · 7 comments · Fixed by #15340
Assignees
Labels
area/qute The template engine kind/bug Something isn't working
Milestone

Comments

@derkoe
Copy link

derkoe commented Feb 25, 2021

Describe the bug
When referencing a boolean property value Qute now calls the isXxx() method instead of referencing the property directly.

Example entity:

@Entity
@Table(name = "todos")
public class Todo extends PanacheEntityBase {

  @Id
  @GeneratedValue
  public UUID id;

  @FormParam
  public String title;

  public Boolean completed = Boolean.FALSE;
}

Example template:

{todo.completed}

Expected behavior
Panache entities have public properties - this should be used when there is no bean accessor method.

Actual behavior
The following exception occurs:

java.lang.NoSuchMethodError: 'java.lang.Boolean todos.Todo.isCompleted()'
	at todos.Todo_ValueResolver.resolve(Todo_ValueResolver.zig:121)
	at io.quarkus.qute.EvaluatorImpl.resolve(EvaluatorImpl.java:128)
	at io.quarkus.qute.EvaluatorImpl.resolveReference(EvaluatorImpl.java:79)
	at io.quarkus.qute.EvaluatorImpl.lambda$resolveReference$1(EvaluatorImpl.java:80)
	at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1106)
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2235)
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:143)
	at io.quarkus.qute.EvaluatorImpl.resolveReference(EvaluatorImpl.java:80)
	at io.quarkus.qute.EvaluatorImpl.evaluate(EvaluatorImpl.java:51)
	at io.quarkus.qute.ResolutionContextImpl$ChildResolutionContext.evaluate(ResolutionContextImpl.java:92)
	at io.quarkus.qute.ExpressionNode.resolve(ExpressionNode.java:26)
	at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:134)
	at io.quarkus.qute.SectionHelper$SectionResolutionContext.execute(SectionHelper.java:37)
	at io.quarkus.qute.Parser$1.resolve(Parser.java:1017)
	at io.quarkus.qute.SectionNode.resolve(SectionNode.java:34)
	at io.quarkus.qute.IncludeSectionHelper.lambda$resolve$1(IncludeSectionHelper.java:44)
	at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859)
	at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:883)
	at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2251)
	at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:143)
	at io.quarkus.qute.IncludeSectionHelper.resolve(IncludeSectionHelper.java:36)
	at io.quarkus.qute.SectionNode.resolve(SectionNode.java:34)
	at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:126)
	at io.quarkus.qute.SectionHelper$SectionResolutionContext.execute(SectionHelper.java:47)
	at io.quarkus.qute.LoopSectionHelper.nextElement(LoopSectionHelper.java:96)
	at io.quarkus.qute.LoopSectionHelper.lambda$resolve$1(LoopSectionHelper.java:45)
	at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1106)
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2235)
	at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:143)
	at io.quarkus.qute.LoopSectionHelper.resolve(LoopSectionHelper.java:34)
	at io.quarkus.qute.SectionNode.resolve(SectionNode.java:34)
	at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:134)
	at io.quarkus.qute.InsertSectionHelper.resolve(InsertSectionHelper.java:20)
	at io.quarkus.qute.SectionNode.resolve(SectionNode.java:34)
	at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:134)
	at io.quarkus.qute.SectionHelper$SectionResolutionContext.execute(SectionHelper.java:37)
	at io.quarkus.qute.Parser$1.resolve(Parser.java:1017)
	at io.quarkus.qute.SectionNode.resolve(SectionNode.java:34)
	at io.quarkus.qute.IncludeSectionHelper.resolve(IncludeSectionHelper.java:33)
	at io.quarkus.qute.SectionNode.resolve(SectionNode.java:34)
	at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:126)
	at io.quarkus.qute.SectionHelper$SectionResolutionContext.execute(SectionHelper.java:37)
	at io.quarkus.qute.Parser$1.resolve(Parser.java:1017)
	at io.quarkus.qute.SectionNode.resolve(SectionNode.java:34)
	at io.quarkus.qute.TemplateImpl$TemplateInstanceImpl.renderData(TemplateImpl.java:107)
	at io.quarkus.qute.TemplateImpl$TemplateInstanceImpl.renderAsync(TemplateImpl.java:90)
	at io.quarkus.qute.runtime.TemplateProducer$InjectableTemplateInstanceImpl.renderAsync(TemplateProducer.java:155)
	at io.quarkus.resteasy.qute.runtime.TemplateResponseFilter.filter(TemplateResponseFilter.java:62)
	at org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:361)
	at org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:252)
	at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:101)
	at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:74)
	at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:594)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:524)
	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
	at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
	at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:138)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.access$000(VertxRequestHandler.java:41)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:93)
	at io.quarkus.runtime.CleanableExecutor$CleaningRunnable.run(CleanableExecutor.java:231)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2415)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at java.base/java.lang.Thread.run(Thread.java:829)
	at org.jboss.threads.JBossThread.run(JBossThread.java:501)
Resulted in: java.util.concurrent.CompletionException: java.lang.NoSuchMethodError: 'java.lang.Boolean todos.Todo.isCompleted()'
	at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314)
	at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1113)
	... 67 more
Resulted in: org.jboss.resteasy.spi.UnhandledException: java.util.concurrent.CompletionException: java.lang.NoSuchMethodError: 'java.lang.Boolean todos.Todo.isCompleted()'
	at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:381)
	at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:218)
	at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:614)
	... 19 more

To Reproduce

Here is a sample project to reproduce this: https://github.com/derkoe/quarkus-htmx-todos/

Steps to reproduce the behavior:

  1. Launch application (GitHub Codespaces/VSCode config is included
  2. Add a single todo

Environment (please complete the following information):

  • Output of java -version:
    openjdk version "11.0.11" 2021-04-20
    OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.11+3-202102231818)
    OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.11+3-202102231818, mixed mode)
    
  • Quarkus version or git rev: 1.12.0.Final
  • Build tool (ie. output of mvnw --version or gradlew --version):
     Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
     Maven home: /usr/share/java/maven-3
     Java version: 11.0.11, vendor: AdoptOpenJDK, runtime: /opt/java/openjdk
     Default locale: en_US, platform encoding: UTF-8
     OS name: "linux", version: "4.19.104-microsoft-standard", arch: "amd64", family: "unix"
    

Additional context
This problem does not occur with Quarkus 1.11.3.Final

@derkoe derkoe added the kind/bug Something isn't working label Feb 25, 2021
@quarkus-bot
Copy link

quarkus-bot bot commented Feb 25, 2021

/cc @mkouba

@quarkus-bot quarkus-bot bot added the area/qute The template engine label Feb 25, 2021
@mkouba mkouba self-assigned this Feb 25, 2021
@mkouba
Copy link
Contributor

mkouba commented Feb 25, 2021

Hm, this is a regression caused by a fix for #14918. After the fix we always use the generated getters to access properties of a Panache entity. However, it seems that the generated getters don't follow the naming conventions for boolean properties, i.e. the generated getter for Todo#completed looks like:

@JsonProperty
public Boolean getCompleted() {
  return this.$$_hibernate_read_completed();
}

UPDATE: I need to correct myself - it does follow the naming conventions, but in this case we have java.lang.Boolean and not boolean...

@FroMage would it be possible to use isCompleted() instead? Note that we can't verify the existence of a specific getter when we generate a value resolver because it's not part of the Jandex index.

@derkoe
Copy link
Author

derkoe commented Feb 25, 2021

Tried to use {todo.getCompleted()} - this results in:

property/method [getCompleted()] not found on class [todos.Todo] nor handled by an extension method

@mkouba
Copy link
Contributor

mkouba commented Feb 25, 2021

Tried to use {todo.getCompleted()} - this results in:

property/method [getCompleted()] not found on class [todos.Todo] nor handled by an extension method

Yeah, that's expected because we don't see the generated methods during validation.

Nevermind, I'll try to solve the problem on the Qute side -> we'll use getX() for boolean properties defined on Panache entities. I'll send a PR shortly.

@derkoe
Copy link
Author

derkoe commented Feb 25, 2021

When I try to add the method manually (tried both isCompleted and getCompleted) and use this in the template I get:

property/method [?('completed')] not found on class [boolean] nor handled by an extension method

@mkouba
Copy link
Contributor

mkouba commented Feb 25, 2021

property/method [?('completed')] not found on class [boolean] nor handled by an extension method

How does the expression look like? It looks like an elvis operator is used? Note that there was a regression in primitive types validation fixed in #15161 (to be included in 1.12.1).

In any case, I have a fix and once I have a proper test I'll send a PR.

@derkoe
Copy link
Author

derkoe commented Feb 25, 2021

Yes this is in a ternary:

<li class="{todo.getCompleted() ? 'completed' : ''}" id="item-{todo.id}">

When i change the getter to object it works - so it is #15161

  public Boolean getCompleted() {
    return completed;
  }

Thx for the quick reaction!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/qute The template engine kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants