Skip to content

Commit

Permalink
Qute - introduce "cdi" namespace to inject CDI beans in templates
Browse files Browse the repository at this point in the history
- it is an alias for the current "inject" namespace
- also destroy dependent beans correctly after the expression is
evaluated
  • Loading branch information
mkouba committed Dec 9, 2021
1 parent 6008c39 commit 6034d03
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 16 deletions.
13 changes: 8 additions & 5 deletions docs/src/main/asciidoc/qute-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<template_extension_methods,template extension method>> 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 <<template_extension_methods,template extension method>> 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 <<template_extension_methods,template extension method>> 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.

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -631,7 +632,7 @@ static Match validateNestedExpressions(QuteConfig config, TemplateAnalysis templ
List<TemplateExtensionMethodBuildItem> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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/";

Expand Down Expand Up @@ -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<Object> bean = Arc.container().instance(ctx.getName());
return bean.isAvailable() ? bean.get() : Results.NotFound.from(ctx);
}).build());
Function<EvalContext, Object> cdiFun = new Function<EvalContext, Object>() {

@Override
public Object apply(EvalContext ctx) {
try (InstanceHandle<Object> 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()) {
Expand Down

0 comments on commit 6034d03

Please sign in to comment.