From af4554678be70893c7bc9ca0a8496ac57008f2e0 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Fri, 18 Oct 2024 09:47:57 +0200 Subject: [PATCH] Qute message bundles: change the way message templates are loaded - introduce a dedicated TemplateLocator so that the message templates can be reloaded automatically when a no-restart change occurs during the dev mode - fixes #43944 --- .../deployment/MessageBundleProcessor.java | 13 +++- .../CustomTemplateLocatorTest.java | 2 +- .../io/quarkus/qute/i18n/MessageBundles.java | 24 ++++---- .../qute/i18n/MessageTemplateLocator.java | 59 +++++++++++++++++++ 4 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/MessageTemplateLocator.java diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java index 2a9ff85c3a60c..8b3af1267819e 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java @@ -52,6 +52,7 @@ import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.processor.Annotations; import io.quarkus.arc.processor.BeanInfo; +import io.quarkus.arc.processor.BuiltinScope; import io.quarkus.arc.processor.DotNames; import io.quarkus.deployment.ApplicationArchive; import io.quarkus.deployment.GeneratedClassGizmoAdaptor; @@ -98,6 +99,7 @@ import io.quarkus.qute.i18n.Message; import io.quarkus.qute.i18n.MessageBundle; import io.quarkus.qute.i18n.MessageBundles; +import io.quarkus.qute.i18n.MessageTemplateLocator; import io.quarkus.qute.runtime.MessageBundleRecorder; import io.quarkus.qute.runtime.QuteConfig; import io.quarkus.runtime.LocalesBuildTimeConfig; @@ -115,7 +117,8 @@ public class MessageBundleProcessor { @BuildStep AdditionalBeanBuildItem beans() { - return new AdditionalBeanBuildItem(MessageBundles.class, MessageBundle.class, Message.class, Localized.class); + return new AdditionalBeanBuildItem(MessageBundles.class, MessageBundle.class, Message.class, Localized.class, + MessageTemplateLocator.class); } @BuildStep @@ -349,6 +352,10 @@ void initBundleContext(MessageBundleRecorder recorder, List bundles, BuildProducer syntheticBeans) throws ClassNotFoundException { + if (bundles.isEmpty()) { + return; + } + Map>> bundleInterfaces = new HashMap<>(); for (MessageBundleBuildItem bundle : bundles) { final Class bundleClass = Class.forName(bundle.getDefaultBundleInterface().toString(), true, @@ -372,7 +379,9 @@ void initBundleContext(MessageBundleRecorder recorder, MessageBundleMethodBuildItem::getTemplate)); syntheticBeans.produce(SyntheticBeanBuildItem.configure(MessageBundleRecorder.BundleContext.class) - .supplier(recorder.createContext(templateIdToContent, bundleInterfaces)).done()); + .scope(BuiltinScope.DEPENDENT.getInfo()) + .supplier(recorder.createContext(templateIdToContent, bundleInterfaces)) + .done()); } @BuildStep diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/templatelocator/CustomTemplateLocatorTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/templatelocator/CustomTemplateLocatorTest.java index 26ee729793825..0f7cefe0ea19f 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/templatelocator/CustomTemplateLocatorTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/templatelocator/CustomTemplateLocatorTest.java @@ -89,7 +89,7 @@ public void testCheckedTemplate() { @Test public void testLocatorsAreRegisteredAsSingletons() { - assertEquals(4, locatorList.size()); + assertEquals(5, locatorList.size()); } @Singleton diff --git a/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/MessageBundles.java b/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/MessageBundles.java index 6191405d631fc..0b4fc5fcd5ff8 100644 --- a/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/MessageBundles.java +++ b/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/MessageBundles.java @@ -10,8 +10,6 @@ import jakarta.enterprise.inject.Default; import jakarta.enterprise.inject.Instance; -import org.jboss.logging.Logger; - import io.quarkus.arc.Arc; import io.quarkus.arc.ArcContainer; import io.quarkus.arc.InstanceHandle; @@ -31,8 +29,6 @@ public final class MessageBundles { public static final String ATTRIBUTE_LOCALE = TemplateInstance.LOCALE; public static final String DEFAULT_LOCALE = "<>"; - private static final Logger LOGGER = Logger.getLogger(MessageBundles.class); - private MessageBundles() { } @@ -62,11 +58,14 @@ public static T get(Class bundleInterface, Localized localized) { .render()); } - static void setupNamespaceResolvers(@Observes EngineBuilder builder, BundleContext context) { + static void setupNamespaceResolvers(@Observes EngineBuilder builder, Instance context) { + if (!context.isResolvable()) { + return; + } // Avoid injecting "Instance instance" which prevents unused beans removal ArcContainer container = Arc.container(); // For every bundle register a new resolver - for (Entry>> entry : context.getBundleInterfaces().entrySet()) { + for (Entry>> entry : context.get().getBundleInterfaces().entrySet()) { final String bundleName = entry.getKey(); final Map interfaces = new HashMap<>(); Resolver resolver = null; @@ -121,10 +120,15 @@ public String getNamespace() { } } - static void setupMessageTemplates(@Observes Engine engine, BundleContext context) { - for (Entry entry : context.getMessageTemplates().entrySet()) { - LOGGER.debugf("Register template for message [%s]", entry.getKey()); - engine.putTemplate(entry.getKey(), engine.parse(entry.getValue())); + static void preloadMessageTemplates(@Observes Engine engine, Instance context) { + if (!context.isResolvable()) { + return; + } + for (String key : context.get().getMessageTemplates().keySet()) { + Template messageTemplate = engine.getTemplate(key); + if (messageTemplate == null) { + throw new IllegalStateException("Unable to preload message template: " + key); + } } } diff --git a/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/MessageTemplateLocator.java b/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/MessageTemplateLocator.java new file mode 100644 index 0000000000000..dc2bec8f4de7b --- /dev/null +++ b/extensions/qute/runtime/src/main/java/io/quarkus/qute/i18n/MessageTemplateLocator.java @@ -0,0 +1,59 @@ +package io.quarkus.qute.i18n; + +import java.io.Reader; +import java.io.StringReader; +import java.util.Optional; + +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +import io.quarkus.arc.WithCaching; +import io.quarkus.qute.TemplateLocator; +import io.quarkus.qute.Variant; +import io.quarkus.qute.runtime.MessageBundleRecorder.BundleContext; + +@Singleton +public class MessageTemplateLocator implements TemplateLocator { + + @WithCaching // BundleContext is dependent + @Inject + Instance bundleContext; + + @Override + public int getPriority() { + return DEFAULT_PRIORITY - 1; + } + + @Override + public Optional locate(String id) { + if (bundleContext.isResolvable()) { + String template = bundleContext.get().getMessageTemplates().get(id); + if (template != null) { + return Optional.of(new MessageTemplateLocation(template)); + } + } + return Optional.empty(); + } + + static final class MessageTemplateLocation implements TemplateLocation { + + private final String content; + + private MessageTemplateLocation(String content) { + this.content = content; + } + + @Override + public Reader read() { + return new StringReader(content); + } + + @Override + public Optional getVariant() { + return Optional.empty(); + } + + } + +}