From 2c428dc31446ae5c3ce72f8b85359302562b93cb Mon Sep 17 00:00:00 2001 From: eschleb Date: Wed, 23 Oct 2024 09:43:47 +0200 Subject: [PATCH] Fix module start cast exception - Improve performance / code readability --- .../magnolia/dictionary/DictionaryModule.java | 32 +-- .../DictionaryMessageBundlesInstaller.java | 112 ++++----- .../DictionaryMessageBundlesLoader.java | 132 ++++++----- .../DictionaryTranslationServiceImpl.java | 221 +++++++----------- .../NodeNameFilteringPredicate.java | 11 +- .../META-INF/magnolia/magnolia-dictionary.xml | 6 + 6 files changed, 258 insertions(+), 256 deletions(-) diff --git a/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/DictionaryModule.java b/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/DictionaryModule.java index 1ee367a..812a549 100644 --- a/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/DictionaryModule.java +++ b/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/DictionaryModule.java @@ -1,22 +1,21 @@ package com.namics.oss.magnolia.dictionary; -import com.namics.oss.magnolia.dictionary.i18nsystem.DictionaryMessageBundlesInstaller; -import com.namics.oss.magnolia.dictionary.i18nsystem.DictionaryTranslationServiceImpl; import info.magnolia.cms.util.FilteredEventListener; -import info.magnolia.i18nsystem.TranslationService; import info.magnolia.module.ModuleLifecycle; import info.magnolia.module.ModuleLifecycleContext; -import info.magnolia.objectfactory.ComponentProvider; -import info.magnolia.objectfactory.Components; import info.magnolia.observation.WorkspaceEventListenerRegistration; -import info.magnolia.resourceloader.ResourceOrigin; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import java.lang.invoke.MethodHandles; import javax.inject.Inject; import javax.jcr.RepositoryException; import javax.jcr.observation.Event; -import java.lang.invoke.MethodHandles; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.namics.oss.magnolia.dictionary.i18nsystem.DictionaryMessageBundlesInstaller; +import com.namics.oss.magnolia.dictionary.i18nsystem.DictionaryMessageBundlesLoader; public class DictionaryModule implements ModuleLifecycle { private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @@ -26,20 +25,24 @@ public class DictionaryModule implements ModuleLifecycle { private static final String OBSERVATION_PATH = "/"; private final DictionaryMessageBundlesInstaller messagesInstaller; - private WorkspaceEventListenerRegistration.Handle handle; + private final DictionaryMessageBundlesLoader dictionaryMessageBundlesLoader; + private WorkspaceEventListenerRegistration.Handle handle; private boolean loadLabelsOnStartup = true; @Inject - public DictionaryModule(final ComponentProvider componentProvider, final ResourceOrigin resourceOrigin) { - messagesInstaller = componentProvider.newInstance(DictionaryMessageBundlesInstaller.class, resourceOrigin); + public DictionaryModule( + final DictionaryMessageBundlesInstaller dictionaryMessageBundlesInstaller, + final DictionaryMessageBundlesLoader dictionaryMessageBundlesLoader + ) { + this.messagesInstaller = dictionaryMessageBundlesInstaller; + this.dictionaryMessageBundlesLoader = dictionaryMessageBundlesLoader; } @Override public void start(ModuleLifecycleContext moduleLifecycleContext) { try { - DictionaryTranslationServiceImpl translationService = (DictionaryTranslationServiceImpl) Components.getComponent(TranslationService.class); this.handle = WorkspaceEventListenerRegistration - .observe(DictionaryConfiguration.REPOSITORY, OBSERVATION_PATH, new FilteredEventListener(translationService, FilteredEventListener.JCR_SYSTEM_EXCLUDING_PREDICATE)) + .observe(DictionaryConfiguration.REPOSITORY, OBSERVATION_PATH, new FilteredEventListener(dictionaryMessageBundlesLoader, FilteredEventListener.JCR_SYSTEM_EXCLUDING_PREDICATE)) .withSubNodes(true) .withDelay(EVENT_DELAY, EVENT_DELAY_MAX) .withEventTypesMask(Event.NODE_ADDED | Event.NODE_REMOVED | Event.PROPERTY_CHANGED) @@ -52,6 +55,7 @@ public void start(ModuleLifecycleContext moduleLifecycleContext) { LOG.info("Start Dictionary module: Load labels to dictionary"); messagesInstaller.loadLabelsToDictionary(); } + dictionaryMessageBundlesLoader.reload(); } @Override diff --git a/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryMessageBundlesInstaller.java b/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryMessageBundlesInstaller.java index 055928a..57d3574 100644 --- a/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryMessageBundlesInstaller.java +++ b/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryMessageBundlesInstaller.java @@ -1,8 +1,8 @@ package com.namics.oss.magnolia.dictionary.i18nsystem; +import com.machinezoo.noexception.Exceptions; import com.namics.oss.magnolia.dictionary.DictionaryConfiguration; import com.namics.oss.magnolia.dictionary.util.DictionaryUtils; -import com.namics.oss.magnolia.dictionary.util.Lazy; import com.namics.oss.magnolia.dictionary.util.NodeUtil; import com.namics.oss.magnolia.dictionary.util.predicates.NodeNameFilteringPredicate; import com.namics.oss.magnolia.dictionary.util.predicates.SystemNodeFilteringPredicate; @@ -11,6 +11,7 @@ import info.magnolia.resourceloader.ResourceOrigin; import org.apache.commons.io.input.BOMInputStream; import org.apache.commons.lang3.StringUtils; +import org.apache.jackrabbit.commons.predicate.Predicate; import org.apache.jackrabbit.commons.predicate.Predicates; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,88 +25,74 @@ import java.lang.invoke.MethodHandles; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.stream.Stream; public class DictionaryMessageBundlesInstaller { private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); public static final String LAST_LOADED_TIME = "lastLoadedTime"; - private final Lazy messages; + private final I18nResourcesProvider i18nResourcesProvider; + private final ResourceOrigin resourceOrigin; - @Inject + @Inject public DictionaryMessageBundlesInstaller( final I18nResourcesProvider i18nResourcesProvider, - final ResourceOrigin resourceOrigin) { - this.messages = Lazy.of(() -> - loadProperties(i18nResourcesProvider.getI18nResources(resourceOrigin)) - ); - } - - private Properties loadProperties(final Collection resources) { - final Properties properties = new Properties(); - resources.stream().map(this::loadProperties).forEach(properties::putAll); - return properties; - } - - private Properties loadProperties(final Resource resource) { - final Properties properties = new Properties(); - try (InputStream in = resource.openStream()) { - LOG.debug("Loading properties from '{}'", resource); - final Reader inStream = new InputStreamReader(new BOMInputStream(in), StandardCharsets.UTF_8); - properties.load(inStream); - } catch (Exception e) { - LOG.error("Failed to read properties from '{}', skipping properties file...", resource, e); - } - return properties; - } - - public Properties getMessages() { - return messages.get(); + final ResourceOrigin resourceOrigin + ) { + this.i18nResourcesProvider = i18nResourcesProvider; + this.resourceOrigin = resourceOrigin; } public void loadLabelsToDictionary() { - Node dictionaryRoot = NodeUtil.getNodeByPathOrNull(DictionaryConfiguration.REPOSITORY, "/"); + final Node dictionaryRoot = NodeUtil.getNodeByPathOrNull(DictionaryConfiguration.REPOSITORY, "/"); setLastLoadedTime(dictionaryRoot); - List notExpired = new ArrayList<>(); + final Set notExpired = new HashSet<>(); - for (Map.Entry message : getMessages().entrySet()) { - String messageValue = message.getValue().toString(); - String messageName = message.getKey().toString(); - String messageNodeName = DictionaryUtils.getValidMessageNodeName(messageName); + streamPropertyEntries().forEach(message -> { + final String messageValue = message.getValue().toString(); + final String messageName = message.getKey().toString(); + final String messageNodeName = DictionaryUtils.getValidMessageNodeName(messageName); if (!messageName.equals(messageNodeName)) { LOG.info("Label name changed from '{}' to '{}'", messageName, messageNodeName); } // collect list of messages/labels present in the property files notExpired.add(messageNodeName); + createProperty(dictionaryRoot, messageNodeName, messageValue, messageName); + }); - Node labelNode = NodeUtil.getOrCreateNode(dictionaryRoot, messageNodeName, DictionaryConfiguration.NODE_TYPE); - try { - if (propertyNeedsUpdate(labelNode, messageValue)) { - PropertyUtil.setProperty(labelNode, DictionaryConfiguration.Prop.NAME, messageName); - PropertyUtil.setProperty(labelNode, DictionaryConfiguration.Prop.VALUE, messageValue); - PropertyUtil.setProperty(labelNode, DictionaryConfiguration.Prop.EXPIRED, null); - labelNode.getSession().save(); - } - } catch (RepositoryException e) { - LOG.warn("Could not create label: '{}'", messageName, e); - } - } + markExpiredProperties(dictionaryRoot, notExpired); + Exceptions.wrap().run(() -> dictionaryRoot.getSession().save()); + } + private void markExpiredProperties(final Node dictionaryRoot, final Set notExpired) { try { - var expiredNodesPredicate = Predicates.and(new NodeNameFilteringPredicate(notExpired), new SystemNodeFilteringPredicate()); - Iterable expiredNodes = NodeUtil.getNodes(dictionaryRoot, expiredNodesPredicate); + final Predicate expiredNodesPredicate = Predicates.and(new NodeNameFilteringPredicate(notExpired), new SystemNodeFilteringPredicate()); + final Iterable expiredNodes = NodeUtil.getNodes(dictionaryRoot, expiredNodesPredicate); for (Node expiredNode : expiredNodes) { PropertyUtil.setProperty(expiredNode, DictionaryConfiguration.Prop.EXPIRED, true); - expiredNode.getSession().save(); } } catch (RepositoryException e) { LOG.warn("Could not mark expired nodes", e); } } + private void createProperty(final Node dictionaryRoot, final String messageNodeName, final String messageValue, final String messageName) { + final Node labelNode = NodeUtil.getOrCreateNode(dictionaryRoot, messageNodeName, DictionaryConfiguration.NODE_TYPE); + try { + if (propertyNeedsUpdate(labelNode, messageValue)) { + PropertyUtil.setProperty(labelNode, DictionaryConfiguration.Prop.NAME, messageName); + PropertyUtil.setProperty(labelNode, DictionaryConfiguration.Prop.VALUE, messageValue); + PropertyUtil.setProperty(labelNode, DictionaryConfiguration.Prop.EXPIRED, null); + } + } catch (RepositoryException e) { + LOG.warn("Could not create label: '{}'", messageName, e); + } + } + private boolean propertyNeedsUpdate(Node labelNode, String newValue) { - String currentValue = PropertyUtil.getString(labelNode, DictionaryConfiguration.Prop.VALUE); - boolean wasExpired = PropertyUtil.getBoolean(labelNode, DictionaryConfiguration.Prop.EXPIRED, false); + final String currentValue = PropertyUtil.getString(labelNode, DictionaryConfiguration.Prop.VALUE); + final boolean wasExpired = PropertyUtil.getBoolean(labelNode, DictionaryConfiguration.Prop.EXPIRED, false); return wasExpired || !StringUtils.equals(currentValue, newValue); } @@ -117,4 +104,25 @@ private void setLastLoadedTime(Node dictionaryRoot) { LOG.error("Could not set time when labels were last loaded. Manually check labels which should get removed.", e); } } + + private Stream> streamPropertyEntries() { + final Collection resources = i18nResourcesProvider.getI18nResources(resourceOrigin); + return resources.stream() + .map(this::loadProperties) + .flatMap(properties -> + properties.entrySet().stream() + ); + } + + private Properties loadProperties(final Resource resource) { + final Properties properties = new Properties(); + try (InputStream in = resource.openStream()) { + LOG.debug("Loading properties from '{}'", resource); + final Reader inStream = new InputStreamReader(BOMInputStream.builder().setInputStream(in).get(), StandardCharsets.UTF_8); + properties.load(inStream); + } catch (Exception e) { + LOG.error("Failed to read properties from '{}', skipping properties file...", resource, e); + } + return properties; + } } diff --git a/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryMessageBundlesLoader.java b/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryMessageBundlesLoader.java index 9339d44..5c9e86a 100644 --- a/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryMessageBundlesLoader.java +++ b/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryMessageBundlesLoader.java @@ -1,68 +1,96 @@ package com.namics.oss.magnolia.dictionary.i18nsystem; -import com.namics.oss.magnolia.dictionary.DictionaryConfiguration; -import com.namics.oss.magnolia.dictionary.util.LocaleUtils; -import com.namics.oss.magnolia.dictionary.util.NodeUtil; import info.magnolia.context.MgnlContext; +import info.magnolia.context.SystemContext; import info.magnolia.jcr.util.PropertyUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import java.util.HashMap; +import java.util.Collections; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Properties; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import javax.inject.Provider; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.observation.EventIterator; +import javax.jcr.observation.EventListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.machinezoo.noexception.Exceptions; +import com.namics.oss.magnolia.dictionary.DictionaryConfiguration; +import com.namics.oss.magnolia.dictionary.util.LocaleUtils; +import com.namics.oss.magnolia.dictionary.util.NodeUtil; + +@Singleton +public class DictionaryMessageBundlesLoader implements EventListener { + private static final Logger LOG = LoggerFactory.getLogger(DictionaryMessageBundlesLoader.class); + private final Provider systemContextProvider; + private Map messages = Collections.emptyMap(); -/** - * @author mrauch, Namics AG - * @since 11.03.2016 - */ -public class DictionaryMessageBundlesLoader { + @Inject + public DictionaryMessageBundlesLoader(final Provider systemContextProvider) { + this.systemContextProvider = systemContextProvider; + } - private static final Logger LOG = LoggerFactory.getLogger(DictionaryMessageBundlesLoader.class); + public void reload() { + Exceptions.wrap().run(() -> { + final Session session = systemContextProvider.get().getJCRSession(DictionaryConfiguration.REPOSITORY); + loadMessages(session); + session.logout(); + }); + } - private final Map messages = new HashMap(); + private void loadMessages(final Session session) throws RepositoryException { + final Node root = session.getRootNode(); + messages = LocaleUtils.getLocalesOfAllSiteDefinitions().stream().collect(Collectors.toMap( + Function.identity(), + locale -> getProperties(locale, root)) + ); + } - public DictionaryMessageBundlesLoader() { - loadMessagesInSystemContext(); - } + private Properties getProperties(final Locale locale, final Node root) { + LOG.debug("Loading dictionary properties with locale [{}]...", locale); + final Properties properties = new Properties(); + streamMessages(locale, root).forEach(entry -> + properties.put(entry.getKey(), entry.getValue()) + ); + return properties; + } - protected void loadMessagesInSystemContext() { - try { - MgnlContext.doInSystemContext(new MgnlContext.RepositoryOp() { - @Override - public void doExec() throws RepositoryException { - loadMessages(); - } - }); - } catch (RepositoryException e) { - LOG.warn("An error occurred while trying to to load dictionary properties", e); - } - } + private Stream> streamMessages(final Locale locale, final Node root) { + return StreamSupport + .stream( + Spliterators.spliteratorUnknownSize(Exceptions.wrap().get(() -> NodeUtil.getNodes(root)).iterator(), Spliterator.ORDERED), + false + ) + .map(message -> + Optional.ofNullable(PropertyUtil.getString(message, LocaleUtils.getLocaleString(locale))).map(value -> + Map.entry(NodeUtil.getName(message), value) + ) + ) + .flatMap(Optional::stream); + } - protected void loadMessages() { - Node root = NodeUtil.getNodeByPathOrNull(DictionaryConfiguration.REPOSITORY, "/"); - for (Locale locale : LocaleUtils.getLocalesOfAllSiteDefinitions()) { - try { - Properties properties = new Properties(); - for (Node message : NodeUtil.asList(NodeUtil.getNodes(root))) { - String key = message.getName(); - String value = PropertyUtil.getString(message, LocaleUtils.getLocaleString(locale)); - if (key != null && value != null) { - properties.put(key, value); - } - } - messages.put(locale, properties); - } catch (RepositoryException e) { - LOG.warn("An error occurred while trying to to load dictionary properties with locale[{}]", locale, e); - } - LOG.debug("Loading dictionary properties with locale [{}]...", locale); - } - } + public Map getMessages() { + return messages; + } - public Map getMessages() { - return messages; - } + @Override + public void onEvent(EventIterator events) { + if (events.getSize() > 0) { + reload(); + } + } } diff --git a/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryTranslationServiceImpl.java b/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryTranslationServiceImpl.java index 2bd4f40..942cc07 100644 --- a/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryTranslationServiceImpl.java +++ b/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/i18nsystem/DictionaryTranslationServiceImpl.java @@ -2,28 +2,19 @@ import info.magnolia.cms.i18n.DefaultMessagesManager; import info.magnolia.cms.i18n.I18nContentSupport; -import info.magnolia.i18nsystem.DefaultMessageBundlesLoader; import info.magnolia.i18nsystem.I18nText; import info.magnolia.i18nsystem.LocaleProvider; import info.magnolia.i18nsystem.TranslationService; import info.magnolia.i18nsystem.TranslationServiceImpl; -import info.magnolia.i18nsystem.module.I18nModule; import info.magnolia.module.site.Site; import info.magnolia.module.site.SiteManager; -import info.magnolia.objectfactory.ComponentProvider; -import info.magnolia.objectfactory.Components; -import info.magnolia.resourceloader.ResourceOrigin; import java.lang.invoke.MethodHandles; import java.util.Arrays; import java.util.Locale; -import java.util.Objects; import java.util.Optional; -import java.util.Properties; import javax.inject.Inject; -import javax.inject.Provider; -import javax.inject.Singleton; import javax.jcr.observation.EventIterator; import javax.jcr.observation.EventListener; @@ -33,128 +24,94 @@ import com.namics.oss.magnolia.dictionary.util.DictionaryUtils; -/** - * @author mrauch, Namics AG - * @since 11.03.2016 - */ -@Singleton public class DictionaryTranslationServiceImpl implements TranslationService, EventListener { - - private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - private final TranslationService translationService; - private final ComponentProvider componentProvider; - private final ResourceOrigin resourceOrigin; - private final DefaultMessagesManager defaultMessagesManager; - - private DictionaryMessageBundlesLoader dictionaryMessageBundles; - - @Inject - public DictionaryTranslationServiceImpl(final Provider i18nModuleProvider, - final Provider defaultMessageBundlesLoaderProvider, - final ComponentProvider componentProvider, - final ResourceOrigin resourceOrigin, - final DefaultMessagesManager defaultMessagesManager) { - this.componentProvider = componentProvider; - this.resourceOrigin = resourceOrigin; - this.defaultMessagesManager = defaultMessagesManager; - this.translationService = new TranslationServiceImpl(i18nModuleProvider, defaultMessageBundlesLoaderProvider); - } - - protected DictionaryMessageBundlesLoader getDictionaryMessageBundles() { - if (dictionaryMessageBundles == null) { - dictionaryMessageBundles = componentProvider.newInstance(DictionaryMessageBundlesLoader.class, resourceOrigin); - } - return dictionaryMessageBundles; - } - - @Override - public String translate(LocaleProvider localeProvider, String[] keys) { - return translate(localeProvider, keys, I18nText.NO_FALLBACK); - } - - @Override - public String translate(LocaleProvider localeProvider, String[] keys, String fallback) { - final Locale locale = localeProvider.getLocale(); - if (locale == null) { - throw new IllegalArgumentException("Locale can't be null"); - } - if (ArrayUtils.isEmpty(keys)) { - throw new IllegalArgumentException("Keys can't be null or empty"); - } - - final String message = lookUpKeyInDictionary(keys, locale); - if (message != null) { - return message; - } - // not found in dictionary, translate using Magnolia's default service - return translationService.translate(localeProvider, keys, fallback); - } - - @Override - @Deprecated - public String translate(LocaleProvider localeProvider, String basename, String[] keys) { - if (basename != null) { - LOG.debug("Dictionary ignores explicit basename ('{}') for keys '{}'", basename, Arrays.asList(keys)); - } - return translate(localeProvider, keys); - } - - @Override - @Deprecated - public void reloadMessageBundles() { - resetMessageBundles(); - } - - protected String lookUpKeyInDictionary(String[] keys, Locale locale) { - LOG.trace("Looking up in dictionary message bundle with key '{}' and Locale '{}'", Arrays.asList(keys), locale); - // fallback chain similar to info.magnolia.i18nsystem.TranslationServiceImpl.lookUpKeyUntilFound - return doGetMessage(keys, locale) - .or(() -> doGetMessage(keys, new Locale(locale.getLanguage(), locale.getCountry()))) - .or(() -> doGetMessage(keys, new Locale(locale.getLanguage()))) - .or(() -> doGetMessage(keys, getSiteFallbackLocale())) - .or(() -> doGetMessage(keys, getFallbackLocale())) - .orElse(null); - } - - protected Optional doGetMessage(String[] keys, Locale locale) { - Optional properties = Optional.ofNullable(getDictionaryMessageBundles()) - .map(DictionaryMessageBundlesLoader::getMessages) - .map(mapper -> mapper.get(locale)); - - return Arrays.stream(keys) - .map(DictionaryUtils::getValidMessageNodeName) - .map(nodeName -> properties.map(props -> props.getProperty(nodeName)).orElse(null)) - .filter(Objects::nonNull) - .findFirst(); - } - - private Locale getSiteFallbackLocale() { - return getSiteI18n() - .map(I18nContentSupport::getFallbackLocale) - .orElse(null); - } - - private Optional getSiteI18n() { - return Optional.of(Components.getComponent(SiteManager.class).getCurrentSite()).map(Site::getI18n); - } - - private Locale getFallbackLocale() { - return defaultMessagesManager.getDefaultLocale(); - } - - private void resetMessageBundles() { - dictionaryMessageBundles = null; - } - - @Override - public void onEvent(EventIterator events) { - if (events.getSize() > 0) { - resetMessageBundles(); - } - } - - public TranslationService getTranslationService() { - return this.translationService; - } + private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private final TranslationService wrapper; + private final SiteManager siteManager; + private final DefaultMessagesManager defaultMessagesManager; + private final DictionaryMessageBundlesLoader dictionaryMessageBundlesLoader; + + @Inject + public DictionaryTranslationServiceImpl( + final TranslationServiceImpl wrapper, + final DictionaryMessageBundlesLoader dictionaryMessageBundlesLoader, + final DefaultMessagesManager defaultMessagesManager, + final SiteManager siteManager + ) { + this.dictionaryMessageBundlesLoader = dictionaryMessageBundlesLoader; + this.defaultMessagesManager = defaultMessagesManager; + this.wrapper = wrapper; + this.siteManager = siteManager; + } + + @Override + public String translate(final LocaleProvider localeProvider, final String[] keys) { + return translate(localeProvider, keys, I18nText.NO_FALLBACK); + } + + @Override + public String translate(final LocaleProvider localeProvider, final String[] keys, final String fallback) { + final Locale locale = localeProvider.getLocale(); + if (locale == null) { + throw new IllegalArgumentException("Locale can't be null"); + } + if (ArrayUtils.isEmpty(keys)) { + throw new IllegalArgumentException("Keys can't be null or empty"); + } + return lookUpKeyInDictionary(keys, locale).orElseGet(() -> + // not found in dictionary, translate using Magnolia's default service + wrapper.translate(localeProvider, keys, fallback) + ); + } + + @Override + @Deprecated + public String translate(final LocaleProvider localeProvider, final String basename, final String[] keys) { + if (basename != null) { + LOG.debug("Dictionary ignores explicit basename ('{}') for keys '{}'", basename, Arrays.asList(keys)); + } + return translate(localeProvider, keys); + } + + @Override + @Deprecated + public void reloadMessageBundles() { + dictionaryMessageBundlesLoader.reload(); + } + + protected Optional lookUpKeyInDictionary(final String[] keys, final Locale locale) { + LOG.trace("Looking up in dictionary message bundle with key '{}' and Locale '{}'", Arrays.asList(keys), locale); + // fallback chain similar to info.magnolia.i18nsystem.TranslationServiceImpl.lookUpKeyUntilFound + return doGetMessage(keys, locale) + .or(() -> doGetMessage(keys, new Locale(locale.getLanguage(), locale.getCountry()))) + .or(() -> doGetMessage(keys, new Locale(locale.getLanguage()))) + .or(() -> getSiteFallbackLocale().flatMap(fallbackLocale -> doGetMessage(keys, fallbackLocale))) + .or(() -> doGetMessage(keys, defaultMessagesManager.getDefaultLocale())); + } + + protected Optional doGetMessage(final String[] keys, final Locale locale) { + return Optional + .ofNullable(dictionaryMessageBundlesLoader.getMessages().get(locale)) + .stream() + .flatMap(properties -> + Arrays.stream(keys) + .map(DictionaryUtils::getValidMessageNodeName) + .map(properties::getProperty) + ) + .findFirst(); + } + + private Optional getSiteFallbackLocale() { + return Optional.of(siteManager.getCurrentSite()) + .map(Site::getI18n) + .map(I18nContentSupport::getFallbackLocale); + } + + @Override + public void onEvent(EventIterator events) { + if (events.getSize() > 0) { + dictionaryMessageBundlesLoader.reload(); + } + } } diff --git a/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/util/predicates/NodeNameFilteringPredicate.java b/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/util/predicates/NodeNameFilteringPredicate.java index 622b84e..2a635b2 100644 --- a/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/util/predicates/NodeNameFilteringPredicate.java +++ b/magnolia-dictionary/src/main/java/com/namics/oss/magnolia/dictionary/util/predicates/NodeNameFilteringPredicate.java @@ -5,20 +5,19 @@ import javax.jcr.Node; import java.util.List; +import java.util.Set; public class NodeNameFilteringPredicate implements Predicate { + private final Set filterNames; - private List filterNames; - - public NodeNameFilteringPredicate(List filterNames) { + public NodeNameFilteringPredicate(final Set filterNames) { this.filterNames = filterNames; } @Override - public boolean evaluate(Object o) { + public boolean evaluate(final Object o) { if (o instanceof Node) { - Node node = (Node) o; - String nodeName = NodeUtil.getName(node); + String nodeName = NodeUtil.getName((Node) o); return !filterNames.contains(nodeName); } return false; diff --git a/magnolia-dictionary/src/main/resources/META-INF/magnolia/magnolia-dictionary.xml b/magnolia-dictionary/src/main/resources/META-INF/magnolia/magnolia-dictionary.xml index 1cac805..88a6ea4 100644 --- a/magnolia-dictionary/src/main/resources/META-INF/magnolia/magnolia-dictionary.xml +++ b/magnolia-dictionary/src/main/resources/META-INF/magnolia/magnolia-dictionary.xml @@ -13,6 +13,12 @@ info.magnolia.i18nsystem.TranslationService com.namics.oss.magnolia.dictionary.i18nsystem.DictionaryTranslationServiceImpl + singleton + + + com.namics.oss.magnolia.dictionary.i18nsystem.DictionaryMessageBundlesLoader + com.namics.oss.magnolia.dictionary.i18nsystem.DictionaryMessageBundlesLoader + singleton com.namics.oss.magnolia.dictionary.i18nsystem.I18nResourcesProvider