Skip to content

Commit

Permalink
Qute - fix a regression introduced in quarkusio#32653 (3.0.1)
Browse files Browse the repository at this point in the history
- value resolvers are not registered for non-application classes after
hot reload
- fixes quarkusio#32959
  • Loading branch information
mkouba committed Apr 29, 2023
1 parent 2c6c42b commit 157aac4
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1742,11 +1742,14 @@ public String apply(String name) {
}
});

// NOTE: We can't use this optimization for classes generated by ValueResolverGenerator because we cannot easily
// map a target class to a specific set of generated classes
ExistingValueResolvers existingValueResolvers = liveReloadBuildItem.getContextObject(ExistingValueResolvers.class);
if (existingValueResolvers == null) {
existingValueResolvers = new ExistingValueResolvers();
liveReloadBuildItem.setContextObject(ExistingValueResolvers.class, existingValueResolvers);
}
Set<String> generatedValueResolvers = new HashSet<>();

ValueResolverGenerator.Builder builder = ValueResolverGenerator.builder()
.setIndex(index).setClassOutput(classOutput);
Expand All @@ -1770,15 +1773,11 @@ public Function<FieldInfo, String> apply(ClassInfo clazz) {
Set<DotName> controlled = new HashSet<>();
Map<DotName, AnnotationInstance> uncontrolled = new HashMap<>();
for (TemplateDataBuildItem data : templateData) {
processTemplateData(data, controlled, uncontrolled, builder, existingValueResolvers, applicationClassPredicate);
processTemplateData(data, controlled, uncontrolled, builder);
}

for (ImplicitValueResolverBuildItem implicit : implicitClasses) {
DotName implicitClassName = implicit.getClazz().name();
if (existingValueResolvers.contains(implicitClassName)) {
// A non-application value resolver already generated
continue;
}
if (controlled.contains(implicitClassName)) {
LOGGER.debugf("Implicit value resolver for %s ignored: class is annotated with @TemplateData",
implicitClassName);
Expand All @@ -1790,24 +1789,23 @@ public Function<FieldInfo, String> apply(ClassInfo clazz) {
continue;
}
builder.addClass(implicit.getClazz(), implicit.getTemplateData());
existingValueResolvers.add(implicitClassName, applicationClassPredicate);
}

ValueResolverGenerator generator = builder.build();
generator.generate();

Set<String> generatedValueResolvers = new HashSet<>();
generatedValueResolvers.addAll(generator.getGeneratedTypes());

ExtensionMethodGenerator extensionMethodGenerator = new ExtensionMethodGenerator(index, classOutput);
Map<DotName, Map<String, List<TemplateExtensionMethodBuildItem>>> classToNamespaceExtensions = new HashMap<>();
Map<String, DotName> namespaceToClass = new HashMap<>();

for (TemplateExtensionMethodBuildItem templateExtension : templateExtensionMethods) {
if (existingValueResolvers.contains(templateExtension.getMethod())) {
String generatedValueResolverClass = existingValueResolvers.getGeneratedClass(templateExtension.getMethod());
if (generatedValueResolverClass != null) {
// A ValueResolver of a non-application class was already generated
generatedValueResolvers.add(generatedValueResolverClass);
continue;
}
existingValueResolvers.add(templateExtension.getMethod(), applicationClassPredicate);

if (templateExtension.hasNamespace()) {
// Group extension methods declared on the same class by namespace
Expand Down Expand Up @@ -1835,8 +1833,10 @@ public Function<FieldInfo, String> apply(ClassInfo clazz) {
namespaceMethods.add(templateExtension);
} else {
// Generate ValueResolver per extension method
extensionMethodGenerator.generate(templateExtension.getMethod(), templateExtension.getMatchName(),
String generatedClass = extensionMethodGenerator.generate(templateExtension.getMethod(),
templateExtension.getMatchName(),
templateExtension.getMatchNames(), templateExtension.getMatchRegex(), templateExtension.getPriority());
existingValueResolvers.add(templateExtension.getMethod(), generatedClass, applicationClassPredicate);
}
}

Expand All @@ -1853,6 +1853,10 @@ public Function<FieldInfo, String> apply(ClassInfo clazz) {
try (NamespaceResolverCreator namespaceResolverCreator = extensionMethodGenerator
.createNamespaceResolver(priorityEntry.getValue().get(0).getMethod().declaringClass(),
nsEntry.getKey(), priorityEntry.getKey())) {
for (TemplateExtensionMethodBuildItem extensionMethod : priorityEntry.getValue()) {
existingValueResolvers.add(extensionMethod.getMethod(), namespaceResolverCreator.getClassName(),
applicationClassPredicate);
}
try (ResolveCreator resolveCreator = namespaceResolverCreator.implementResolve()) {
for (TemplateExtensionMethodBuildItem method : priorityEntry.getValue()) {
resolveCreator.addMethod(method.getMethod(), method.getMatchName(), method.getMatchNames(),
Expand Down Expand Up @@ -1901,28 +1905,25 @@ public Function<FieldInfo, String> apply(ClassInfo clazz) {
*/
static class ExistingValueResolvers {

final Set<String> identifiers = new HashSet<>();

boolean contains(DotName className) {
return identifiers.contains(className.toString());
}
final Map<String, String> identifiersToGeneratedClass = new HashMap<>();

boolean contains(MethodInfo extensionMethod) {
return identifiers.contains(extensionMethod.declaringClass().toString() + "#" + extensionMethod.toString());
return identifiersToGeneratedClass
.containsKey(toKey(extensionMethod));
}

boolean add(DotName className, Predicate<DotName> applicationClassPredicate) {
if (!applicationClassPredicate.test(className)) {
return identifiers.add(className.toString());
}
return false;
String getGeneratedClass(MethodInfo extensionMethod) {
return identifiersToGeneratedClass.get(toKey(extensionMethod));
}

boolean add(MethodInfo extensionMethod, Predicate<DotName> applicationClassPredicate) {
void add(MethodInfo extensionMethod, String className, Predicate<DotName> applicationClassPredicate) {
if (!applicationClassPredicate.test(extensionMethod.declaringClass().name())) {
return identifiers.add(extensionMethod.declaringClass().toString() + "#" + extensionMethod.toString());
identifiersToGeneratedClass.put(toKey(extensionMethod), className);
}
return false;
}

private String toKey(MethodInfo extensionMethod) {
return extensionMethod.declaringClass().toString() + "#" + extensionMethod.toString();
}
}

Expand Down Expand Up @@ -2942,22 +2943,15 @@ private static boolean methodMatches(MethodInfo method, VirtualMethodPart virtua
}

private void processTemplateData(TemplateDataBuildItem templateData,
Set<DotName> controlled, Map<DotName, AnnotationInstance> uncontrolled, ValueResolverGenerator.Builder builder,
ExistingValueResolvers existingValueResolvers,
CompletedApplicationClassPredicateBuildItem applicationClassPredicate) {
Set<DotName> controlled, Map<DotName, AnnotationInstance> uncontrolled, ValueResolverGenerator.Builder builder) {
DotName targetClassName = templateData.getTargetClass().name();
if (existingValueResolvers.contains(targetClassName)) {
return;
}
if (templateData.isTargetAnnotatedType()) {
controlled.add(targetClassName);
builder.addClass(templateData.getTargetClass(), templateData.getAnnotationInstance());
existingValueResolvers.add(targetClassName, applicationClassPredicate);
} else {
// At this point we can be sure that multiple unequal @TemplateData do not exist for a specific target
uncontrolled.computeIfAbsent(targetClassName, name -> {
builder.addClass(templateData.getTargetClass(), templateData.getAnnotationInstance());
existingValueResolvers.add(targetClassName, applicationClassPredicate);
return templateData.getAnnotationInstance();
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.quarkus.qute.deployment.devmode;

import static io.restassured.RestAssured.given;

import org.hamcrest.Matchers;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusDevModeTest;

/**
* Test that built-in extension value resolvers are correctly registered after live reload.
*/
public class ExistingValueResolversDevModeTest {

@RegisterExtension
static final QuarkusDevModeTest config = new QuarkusDevModeTest()
.withApplicationRoot(root -> root
.addClass(TestRoute.class)
.addAsResource(new StringAsset(
"{#let a = 3}{#let b = a.minus(2)}b={b}{/}{/}"),
"templates/let.html"));

@Test
public void testExistingValueResolvers() {
given().get("test")
.then()
.statusCode(200)
.body(Matchers.equalTo("b=1"));

config.modifyResourceFile("templates/let.html", t -> t.concat("::MODIFIED"));

given().get("test")
.then()
.statusCode(200)
.body(Matchers.equalTo("b=1::MODIFIED"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.quarkus.qute.deployment.devmode;

import jakarta.inject.Inject;

import io.quarkus.qute.Template;
import io.quarkus.vertx.web.Route;
import io.vertx.ext.web.RoutingContext;

public class TestRoute {

@Inject
Template let;

@Route(path = "test")
public void test(RoutingContext ctx) {
ctx.end(let.render());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,16 @@ public static void validate(MethodInfo method, String namespace) {
}
}

public void generate(MethodInfo method, String matchName, List<String> matchNames, String matchRegex, Integer priority) {
/**
*
* @param method
* @param matchName
* @param matchNames
* @param matchRegex
* @param priority
* @return the fully qualified name of the generated class
*/
public String generate(MethodInfo method, String matchName, List<String> matchNames, String matchRegex, Integer priority) {

AnnotationInstance extensionAnnotation = method.annotation(TEMPLATE_EXTENSION);
List<Type> parameters = method.parameterTypes();
Expand Down Expand Up @@ -199,6 +208,7 @@ public void generate(MethodInfo method, String matchName, List<String> matchName
implementResolve(valueResolver, declaringClass, method, matchName, matchNames, patternField, params);

valueResolver.close();
return generatedName.replace('/', '.');
}

public NamespaceResolverCreator createNamespaceResolver(ClassInfo declaringClass, String namespace, int priority) {
Expand Down Expand Up @@ -449,6 +459,10 @@ public NamespaceResolverCreator(ClassInfo declaringClass, String namespace, int
implementGetPriority(namespaceResolver, priority);
}

public String getClassName() {
return namespaceResolver.getClassName().replace('/', '.');
}

public ResolveCreator implementResolve() {
return new ResolveCreator();
}
Expand Down

0 comments on commit 157aac4

Please sign in to comment.