diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteDevModeProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteDevModeProcessor.java index efb936fd9da38..40cce54b35177 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteDevModeProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteDevModeProcessor.java @@ -53,7 +53,7 @@ public boolean test(String t) { return false; } }); - try (ClassCreator classCreator = ClassCreator.builder().className("io.quarkus.qute.test.QuteDummyGlobals") + try (ClassCreator classCreator = ClassCreator.builder().className("org.acme.qute.test.QuteDummyGlobals") .classOutput(gizmoAdaptor).build()) { classCreator.addAnnotation(TemplateGlobal.class); 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 8e7de3c2924cc..f758d84af02ce 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 @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -2095,16 +2096,26 @@ public Function apply(ClassInfo clazz) { if (!templateGlobals.isEmpty()) { Set generatedGlobals = new HashSet<>(); - // The initial priority is increased during live reload so that priorities of non-application globals - // do not conflict with priorities of application globals - int initialPriority = -1000 + existingValueResolvers.globals.size(); + // The classes for non-application globals are only generated during the first run because they can't be reloaded + // by the class loader during hot reload + // However, we need to make sure the priorities used by non-application globals do not conflict + // with priorities of application globals that are regenerated during each hot reload + // Therefore, the initial priority is increased by the number of all globals ever found + // For example, if there are three globals [A, B, C] (A and C are non-application classes) + // The intial priority during the first hot reload will be "-1000 + 3 = 997" + // If a global D is added afterwards, the initial priority during the subsequent hot reload will be "-1000 + 4 = 996" + // If the global D is removed, the initial priority will still remain "-1000 + 4 = 996" + // This way we can be sure that the priorities assigned to A and C will never conflict with priorities of B and D or any other application global class + int initialPriority = -1000 + existingValueResolvers.allGlobals.size(); TemplateGlobalGenerator globalGenerator = new TemplateGlobalGenerator(classOutput, GLOBAL_NAMESPACE, initialPriority, index); - Map> classToTargets = new HashMap<>(); + Map> classToTargets = new LinkedHashMap<>(); Map> classToGlobals = templateGlobals.stream() - .collect(Collectors.groupingBy(TemplateGlobalBuildItem::getDeclaringClass)); + .sorted(Comparator.comparing(g -> g.getDeclaringClass())) + .collect(Collectors.groupingBy(TemplateGlobalBuildItem::getDeclaringClass, LinkedHashMap::new, + Collectors.toList())); for (Entry> entry : classToGlobals.entrySet()) { classToTargets.put(entry.getKey(), entry.getValue().stream().collect( Collectors.toMap(TemplateGlobalBuildItem::getName, TemplateGlobalBuildItem::getTarget))); @@ -2116,8 +2127,8 @@ public Function apply(ClassInfo clazz) { generatedGlobals.add(generatedClass); } else { generatedClass = globalGenerator.generate(index.getClassByName(e.getKey()), e.getValue()); - existingValueResolvers.addGlobal(e.getKey(), generatedClass, applicationClassPredicate); } + existingValueResolvers.addGlobal(e.getKey(), generatedClass, applicationClassPredicate); } generatedGlobals.addAll(globalGenerator.getGeneratedTypes()); @@ -2135,9 +2146,12 @@ public Function apply(ClassInfo clazz) { static class ExistingValueResolvers { final Map identifiersToGeneratedClass = new HashMap<>(); - // class declaring globals -> generated type + + // class declaring globals -> generated type; non-application globals only final Map globals = new HashMap<>(); + final Set allGlobals = new HashSet<>(); + boolean contains(MethodInfo extensionMethod) { return identifiersToGeneratedClass .containsKey(toKey(extensionMethod)); @@ -2158,7 +2172,7 @@ void add(MethodInfo extensionMethod, String className, Predicate applic } void addGlobal(DotName declaringClassName, String generatedClassName, Predicate applicationClassPredicate) { - if (!applicationClassPredicate.test(declaringClassName)) { + if (allGlobals.add(generatedClassName.toString()) && !applicationClassPredicate.test(declaringClassName)) { globals.put(declaringClassName.toString(), generatedClassName); } } diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/devmode/TemplateGlobalDevModeTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/devmode/TemplateGlobalDevModeTest.java index a9ab34023584a..d3d57177743ea 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/devmode/TemplateGlobalDevModeTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/devmode/TemplateGlobalDevModeTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.qute.TemplateGlobal; import io.quarkus.test.QuarkusDevModeTest; /** @@ -20,9 +21,9 @@ public class TemplateGlobalDevModeTest { @RegisterExtension static final QuarkusDevModeTest config = new QuarkusDevModeTest() .withApplicationRoot(root -> root - .addClasses(TestRoute.class, QuteDummyTemplateGlobalMarker.class) + .addClasses(TestRoute.class, MyGlobals.class, QuteDummyTemplateGlobalMarker.class) .addAsResource(new StringAsset( - "{quteDummyFoo}:{testFoo ?: 'NA'}"), + "{foo}:{quteDummyFoo}:{testFoo ?: 'NA'}"), "templates/test.html")); @Test @@ -30,7 +31,7 @@ public void testTemplateGlobals() { given().get("test") .then() .statusCode(200) - .body(Matchers.equalTo("bar:NA")); + .body(Matchers.equalTo("24:bar:NA")); // Add application globals - the priority sequence should be automatically // increased before it's used for TestGlobals @@ -39,7 +40,14 @@ public void testTemplateGlobals() { given().get("test") .then() .statusCode(200) - .body(Matchers.equalTo("bar:baz")); + .body(Matchers.equalTo("24:bar:baz")); + } + + @TemplateGlobal + public static class MyGlobals { + + public static int foo = 24; + } }