Skip to content

Commit

Permalink
#28251 improve cache to support per key lookups
Browse files Browse the repository at this point in the history
  • Loading branch information
fabrizzio-dotCMS committed May 16, 2024
1 parent 4d350f4 commit d7eabb7
Show file tree
Hide file tree
Showing 11 changed files with 424 additions and 213 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.liferay.portal.model.User;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* Provides access to Language Variable objects in the system, which allow you to associate a key to
Expand Down Expand Up @@ -146,6 +147,15 @@ public List<KeyValue> getAllLanguageVariablesKeyStartsWith(final String key, fin
*/
List<LanguageVariable> findVariables(final long langId) throws DotDataException;

/**
* Returns an Optional of {@link LanguageVariable} matching the specified language ID and key.
* @param languageId - The ID of the language that the variable was created for.
* @param key - The key to the Language Variable that starts with.
* @return Optional of Language Variables.
* @throws DotDataException - If an error occurs while retrieving the Language Variables.
*/
Optional<LanguageVariable> findVariable(final long languageId, final String key) throws DotDataException;

/**
* Returns a list of {@link LanguageVariable} that the key starts with the specified key and
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.vavr.Lazy;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -171,6 +172,25 @@ public List<LanguageVariable> findVariables(final long langId) throws DotDataExc
});
}

/**
* {@inheritDoc}
*/
@CloseDBIfOpened
@Override
public Optional<LanguageVariable> findVariable(final long languageId, final String key) throws DotDataException {
final LanguageVariableFactory factory = FactoryLocator.getLanguageVariableFactory();
final ContentType contentType = langVarContentType.get();
final LanguageCache languageCache = CacheLocator.getLanguageCache();
return languageCache.ifPresentGetOrElseFetch(languageId, key, ()->{
try {
return factory.findVariables(contentType, languageId, 0, 0, null);
} catch (DotDataException e) {
Logger.error(this, "Error finding language variables", e);
return List.of();
}
});
}

/**
* {@inheritDoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.dotcms.contenttype.model.type.ContentType;
import com.dotmarketing.exception.DotDataException;
import java.util.List;
import java.util.Optional;

/**
* LanguageVariableFactory is the Data Access Object (DAO) for LanguageVariable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* Implementation class for the {@link LanguageVariableFactory}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ public Response getAllMessages (
if(UtilMethods.isSet(language1)) {

final LanguageVariableAPI languageVariableAPI = APILocator.getLanguageVariableAPI();
if(languageAPI.isLocalizationEnhancementsEnabled()) {
if(isLocalizationEnhancementsEnabled()) {
final Language matchingLang = languageAPI.getLanguage(currentLocale.getLanguage(),currentLocale.getCountry());
// Enhanced Language Vars
final List<LanguageVariable> variables = languageVariableAPI.findVariables(matchingLang.getId());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.dotmarketing.portlets.languagesmanager.business;

import com.dotcms.content.elasticsearch.business.DotIndexException;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.portlets.languagesmanager.model.Language;
import com.dotmarketing.portlets.languagesmanager.model.LanguageKey;
import com.dotmarketing.util.Config;
import com.liferay.portal.model.User;
import io.vavr.Lazy;
import java.util.Collection;
import java.util.List;
Expand All @@ -10,12 +15,6 @@
import java.util.Optional;
import java.util.Set;

import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.portlets.languagesmanager.model.Language;
import com.dotmarketing.portlets.languagesmanager.model.LanguageKey;
import com.liferay.portal.model.User;

/**
* Provides access to information related to the different languages that can be added to the
* system.
Expand All @@ -32,10 +31,18 @@
*/
public interface LanguageAPI {

String LOCALIZATION_ENHANCEMENTS_ENABLED = "LOCALIZATION_ENHANCEMENTS_ENABLED";
Lazy<Boolean> localizationEnhancementsEnabled = Lazy.of(
() -> Config.getBooleanProperty("LOCALIZATION_ENHANCEMENTS_ENABLED", true));
() -> Config.getBooleanProperty(LOCALIZATION_ENHANCEMENTS_ENABLED, true));

static boolean isLocalizationEnhancementsEnabled() {
//this system property is used to enable/disable the localization enhancements from any integration context
// since the Config class is wrapped within a Lazy object and once it is loaded it is not possible to change the value
final String enabled = System.getProperty(
LOCALIZATION_ENHANCEMENTS_ENABLED);

boolean isLocalizationEnhancementsEnabled();
return enabled != null ? Boolean.parseBoolean(enabled) : localizationEnhancementsEnabled.get();
}

/**
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.dotcms.business.CloseDBIfOpened;
import com.dotcms.business.WrapInTransaction;
import com.dotcms.content.elasticsearch.business.DotIndexException;
import com.dotcms.languagevariable.business.LanguageVariable;
import com.dotcms.languagevariable.business.LanguageVariableAPI;
import com.dotcms.rendering.velocity.util.VelocityUtil;
import com.dotcms.system.event.local.business.LocalSystemEventsAPI;
Expand All @@ -28,7 +29,7 @@
import com.liferay.portal.language.LanguageException;
import com.liferay.portal.language.LanguageUtil;
import com.liferay.portal.model.User;
import io.vavr.Lazy;
import io.vavr.control.Try;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -38,7 +39,6 @@
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BooleanSupplier;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.velocity.tools.view.context.ViewContext;
Expand All @@ -53,23 +53,14 @@
*/
public class LanguageAPIImpl implements LanguageAPI {

private static final LanguageKeyComparator LANGUAGE_KEY_COMPARATOR = new LanguageKeyComparator();

private final static LanguageKeyComparator LANGUAGE_KEY_COMPARATOR = new LanguageKeyComparator();

private HttpServletRequest request; // todo: this should be decouple from the api
private LanguageFactory factory;
private LanguageVariableAPI languageVariableAPI;
private final LocalSystemEventsAPI localSystemEventsAPI = APILocator.getLocalSystemEventsAPI();

private BooleanSupplier localizationEnhancementsEnabledOverride;

public LanguageAPIImpl(
LanguageFactory factory,
LanguageVariableAPI languageVariableAPI,
BooleanSupplier localizationEnhancementsEnabledOverride) {
this.factory = factory;
this.languageVariableAPI = languageVariableAPI;
this.localizationEnhancementsEnabledOverride = localizationEnhancementsEnabledOverride;
}
/**
* Inits the service with the user {@link ViewContext}
* @param obj
Expand All @@ -79,31 +70,13 @@ public void init(final Object obj) {
this.request = context.getRequest(); // todo: this is just getting it for the user, so instead of getting the request should get the user.
}

@Override
public boolean isLocalizationEnhancementsEnabled() {
if(localizationEnhancementsEnabledOverride != null) {
return localizationEnhancementsEnabledOverride.getAsBoolean();
}
return localizationEnhancementsEnabled.get();
}

/**
* Creates a new instance of the {@link LanguageAPI}.
*/
public LanguageAPIImpl() {
this(FactoryLocator.getLanguageFactory(), APILocator.getLanguageVariableAPI(), null);
}

/**
* Creates a new instance of the {@link LanguageAPI}.
* @param localizationEnhancementsEnabledOverride
*/
@VisibleForTesting
public LanguageAPIImpl(BooleanSupplier localizationEnhancementsEnabledOverride) {
this(FactoryLocator.getLanguageFactory(), APILocator.getLanguageVariableAPI(), localizationEnhancementsEnabledOverride);
factory = FactoryLocator.getLanguageFactory();
}


@Override
@WrapInTransaction
public void deleteLanguage(final Language language) {
Expand Down Expand Up @@ -317,18 +290,22 @@ public void saveLanguageKeys(final Language lang, final Map<String, String> gene
@CloseDBIfOpened
@Override
public Map<String, String> getStringsAsMap(final Locale locale, final Collection<String> keys) {
final LanguageVariableAPI langVarsAPI = getLanguageVariableAPI();
final Map<String, String> messagesMap = new HashMap<>();

if (null != keys) {
final Language lang = APILocator.getLanguageAPI().getLanguage(locale.getLanguage(), locale.getCountry());
keys.forEach(messageKey -> {

String message = (lang != null)
? getStringKey(lang, messageKey)
: getStringFromPropertiesFile(locale, messageKey) ;
message = (message == null) ? messageKey : message;
messagesMap.put(messageKey, message);

final Optional<LanguageVariable> variable = Try.of(()->langVarsAPI.findVariable(lang.getId(), messageKey)).getOrElse(Optional.empty());
if(variable.isPresent()){
messagesMap.put(messageKey, variable.get().value());
} else {
String message = (lang != null)
? getStringKey(lang, messageKey)
: getStringFromPropertiesFile(locale, messageKey);
message = (message == null) ? messageKey : message;
messagesMap.put(messageKey, message);
}
});
}

Expand All @@ -338,15 +315,29 @@ public Map<String, String> getStringsAsMap(final Locale locale, final Collection
@CloseDBIfOpened
@Override
public String getStringKey ( final Language lang, final String key ) {
final LanguageVariableAPI langVarsAPI = getLanguageVariableAPI();

final User user = getUser();
final Optional<LanguageVariable> variable = Try.of(()->langVarsAPI.findVariable(lang.getId(), key)).getOrElse(Optional.empty());
if(variable.isPresent()){
return variable.get().value();
}

final User user = getUser();
// First, look it up using the new Language Variable API
final String value = getLanguageVariableAPI().getLanguageVariableRespectingFrontEndRoles(key, lang.getId(), user);
final String value = langVarsAPI.getLanguageVariableRespectingFrontEndRoles(key, lang.getId(), user);
// If not found, retrieve value from legacy Language Variables or the appropriate
final String countryCode = null == lang.getCountryCode()?"":lang.getCountryCode();
return (UtilMethods.isNotSet(value) || value.equals(key)) ? this.getStringFromPropertiesFile(new Locale( lang.getLanguageCode(), countryCode ), key) : value;
}

/**
* This method internally uses MultiLanguageResourceBundle to get the value of the key.
* if the enhancedLanguageVariables is enabled, it will use the LanguageVariableAPI to get the value of the key.
* Therefore, it won't necessarily load the value from the properties file. And we should probably rename this method to getStringKeyFromLanguageVariable
* @param locale
* @param key
* @return
*/
private String getStringFromPropertiesFile (final Locale locale, final String key) {
String value = null;
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.dotmarketing.business.DotCacheException;
import com.dotmarketing.portlets.languagesmanager.model.Language;
import com.dotmarketing.portlets.languagesmanager.model.LanguageKey;
import java.util.Optional;
import java.util.concurrent.Callable;

/**
Expand Down Expand Up @@ -86,7 +87,7 @@ public abstract class LanguageCache implements Cachable{
* @return
*/
public abstract List<LanguageVariable> ifPresentGetOrElseFetch(final long languageId,
Callable<List<LanguageVariable>> fetch) throws DotDataException;
Callable<List<LanguageVariable>> fetchFunction) throws DotDataException;

/**
* Removes the language stored under the key LANGUAGE_KEY_PREFIX + languageId
Expand All @@ -99,4 +100,23 @@ public abstract List<LanguageVariable> ifPresentGetOrElseFetch(final long langua
* @param languageId the language id
*/
public abstract List<LanguageVariable> getVars(final long languageId);

/**
* Removes the language stored under the key LANGUAGE_KEY_PREFIX + languageId
* @param languageId the language id
* @param key the key
* @return the language variable
*/
public abstract Optional<LanguageVariable> getVar(final long languageId, final String key);

/**
* Removes the language stored under the key LANGUAGE_KEY_PREFIX + languageId
* @param languageId the language id
* @param key the key
* @param fetchFunction the fetch function
* @return the language variable
* @throws DotDataException if an error occurs
*/
public abstract Optional<LanguageVariable> ifPresentGetOrElseFetch(final long languageId, final String key,
final Callable<List<LanguageVariable>> fetchFunction) throws DotDataException;
}
Loading

0 comments on commit d7eabb7

Please sign in to comment.