diff --git a/docs/src/main/asciidoc/qute-reference.adoc b/docs/src/main/asciidoc/qute-reference.adoc index b20971e1feacb..ec133bf7c6e82 100644 --- a/docs/src/main/asciidoc/qute-reference.adoc +++ b/docs/src/main/asciidoc/qute-reference.adoc @@ -1191,16 +1191,19 @@ NOTE: When using `quarkus-resteasy-qute` the content negotiation is performed au === Injecting Beans Directly In Templates -A CDI bean annotated with `@Named` can be referenced in any template through the `inject` namespace: +A CDI bean annotated with `@Named` can be referenced in any template through `cdi` and/or `inject` namespaces: [source,html] ---- -{inject:foo.price} <1> +{cdi:personService.findPerson(10).name} <1> +{inject:foo.price} <2> ---- -<1> First, a bean with name `foo` is found and then used as the base object. +<1> First, a bean with name `personService` is found and then used as the base object. +<2> First, a bean with name `foo` is found and then used as the base object. -All expressions using the `inject` namespace are validated during build. -For the expression `inject:foo.price` the implementation class of the injected bean must either have the `price` property (e.g. a `getPrice()` method) or a matching <> must exist. +All expressions with `cdi` and `inject` namespaces are validated during build. +For the expression `cdi:personService.findPerson(10).name` the implementation class of the injected bean must either declare the `findPerson` method or a matching <> must exist. +For the expression `inject:foo.price` the implementation class of the injected bean must either have the `price` property (e.g. a `getPrice()` method) or a matching <> must exist. NOTE: A `ValueResolver` is also generated for all beans annotated with `@Named` so that it's possible to access its properties without reflection. diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java index 3594df36ea84e..8174b3db8f517 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java @@ -1,6 +1,7 @@ package io.quarkus.qute.deployment; import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; +import static io.quarkus.qute.runtime.EngineProducer.CDI_NAMESPACE; import static io.quarkus.qute.runtime.EngineProducer.INJECT_NAMESPACE; import static java.util.function.Predicate.not; import static java.util.stream.Collectors.toMap; @@ -631,7 +632,7 @@ static Match validateNestedExpressions(QuteConfig config, TemplateAnalysis templ List extensionMethods = null; if (namespace != null) { - if (namespace.equals(INJECT_NAMESPACE)) { + if (namespace.equals(INJECT_NAMESPACE) || namespace.equals(CDI_NAMESPACE)) { BeanInfo bean = findBean(expression, index, incorrectExpressions, namedBeans); if (bean != null) { rootClazz = bean.getImplClazz(); diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/inject/InjectNamespaceResolverTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/inject/InjectNamespaceResolverTest.java index 711a38c5c7114..3f6b594c7832f 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/inject/InjectNamespaceResolverTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/inject/InjectNamespaceResolverTest.java @@ -2,8 +2,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.concurrent.atomic.LongAdder; + +import javax.annotation.PreDestroy; import javax.enterprise.context.Dependent; import javax.inject.Inject; +import javax.inject.Named; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.junit.jupiter.api.Test; @@ -17,23 +21,36 @@ public class InjectNamespaceResolverTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() - .withApplicationRoot((jar) -> jar + .withApplicationRoot(root -> root .addClasses(SimpleBean.class, Hello.class) - .addAsResource(new StringAsset("{inject:hello.ping}"), "templates/foo.html")); + .addAsResource( + new StringAsset( + "{inject:hello.ping} != {inject:simple.ping} and {cdi:hello.ping} != {cdi:simple.ping}"), + "templates/foo.html")); @Inject - SimpleBean simpleBean; + Template foo; @Test public void testInjection() { - assertEquals("pong", simpleBean.foo.render()); + assertEquals("pong != simple and pong != simple", foo.render()); + assertEquals(2, SimpleBean.DESTROYS.longValue()); } + @Named("simple") @Dependent public static class SimpleBean { - @Inject - Template foo; + static final LongAdder DESTROYS = new LongAdder(); + + public String ping() { + return "simple"; + } + + @PreDestroy + void destroy() { + DESTROYS.increment(); + } } diff --git a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java index f01e316cdd49a..41b78bff1db1f 100644 --- a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java +++ b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java @@ -8,6 +8,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; +import java.util.function.Function; import java.util.regex.Pattern; import javax.enterprise.context.ApplicationScoped; @@ -22,6 +23,7 @@ import io.quarkus.arc.InstanceHandle; import io.quarkus.qute.Engine; import io.quarkus.qute.EngineBuilder; +import io.quarkus.qute.EvalContext; import io.quarkus.qute.HtmlEscaper; import io.quarkus.qute.NamespaceResolver; import io.quarkus.qute.ReflectionValueResolver; @@ -41,6 +43,7 @@ public class EngineProducer { public static final String INJECT_NAMESPACE = "inject"; + public static final String CDI_NAMESPACE = "cdi"; private static final String TAGS = "tags/"; @@ -132,10 +135,17 @@ public EngineProducer(QuteContext context, QuteConfig config, QuteRuntimeConfig builderReady.fire(builder); // Resolve @Named beans - builder.addNamespaceResolver(NamespaceResolver.builder(INJECT_NAMESPACE).resolve(ctx -> { - InstanceHandle bean = Arc.container().instance(ctx.getName()); - return bean.isAvailable() ? bean.get() : Results.NotFound.from(ctx); - }).build()); + Function cdiFun = new Function() { + + @Override + public Object apply(EvalContext ctx) { + try (InstanceHandle bean = Arc.container().instance(ctx.getName())) { + return bean.isAvailable() ? bean.get() : Results.NotFound.from(ctx); + } + } + }; + builder.addNamespaceResolver(NamespaceResolver.builder(INJECT_NAMESPACE).resolve(cdiFun).build()); + builder.addNamespaceResolver(NamespaceResolver.builder(CDI_NAMESPACE).resolve(cdiFun).build()); // Add generated resolvers for (String resolverClass : context.getResolverClasses()) {