Skip to content

Commit

Permalink
feat: Introduce User locale in Spring based Rest requests - MEED-6843 -
Browse files Browse the repository at this point in the history
Meeds-io/MIPs#133

This change will introduce the User locale in Spring Based Rest endpoints. To do so, a new 'web' module has been added to group all used filters in Spring Rest Contexts.
  • Loading branch information
boubaker committed May 22, 2024
1 parent b72a29c commit da21042
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
import org.exoplatform.container.component.ComponentRequestLifecycle;
import org.exoplatform.container.component.RequestLifeCycle;
import org.exoplatform.portal.Constants;
import org.exoplatform.portal.config.UserPortalConfigService;
import org.exoplatform.portal.config.model.PortalConfig;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.organization.OrganizationService;
Expand All @@ -23,14 +21,16 @@
* This class is used to ease {@link LocaleContextInfo} object build
*/
public class LocaleContextInfoUtils {

private static final String LOCALE_COOKIE = "LOCALE";

private static final String PREV_LOCALE_SESSION_ATTR = "org.gatein.LAST_LOCALE";

private static final String LOCALE_SESSION_ATTR = "org.gatein.LOCALE";

private static final Log LOG = ExoLogger.getLogger(LocaleContextInfoUtils.class);

private static final String LOCALE_COOKIE = "LOCALE";

private static final String PREV_LOCALE_SESSION_ATTR = "org.gatein.LAST_LOCALE";

private static final String LOCALE_SESSION_ATTR = "org.gatein.LOCALE";

private static final Log LOG = ExoLogger.getLogger(LocaleContextInfoUtils.class);

private static final Set<Locale> SUPPORTED_LOCALES = new HashSet<>();

/**
* Computes locale of currently authenticated user based on multiple
Expand Down Expand Up @@ -64,24 +64,24 @@ public static Locale computeLocale(HttpServletRequest request) {
}

/**
* Helper method for setters invocation on {@link LocaleContextInfo} object
* Helper method for setters invocation on {@link LocaleContextInfo} object
*
* @param request
* @return a built {@link LocaleContextInfo} object
*/
public static LocaleContextInfo buildLocaleContextInfo(HttpServletRequest request) {
LocaleContextInfo localeCtx = new LocaleContextInfo();
// start with setting supported locales and and portal locale
localeCtx.setSupportedLocales(getSupportedLocales());
localeCtx.setPortalLocale(getPortalLocale());
// check request nullability before proceeding
if (request == null) {
return localeCtx;
}
//
String username = request.getRemoteUser();
// get session locale
String lastLocaleLangauge = getPreviousLocale(request) == null ? null : getPreviousLocale(request).toString();
Locale sessionLocale = lastLocaleLangauge == null ? getSessionLocale(request) : LocaleUtils.toLocale(lastLocaleLangauge);
Locale previousLocale = getPreviousLocale(request);
Locale sessionLocale = previousLocale == null ? getSessionLocale(request) : previousLocale;
localeCtx.setSessionLocale(sessionLocale);
// continue setting localCtx with data fetched from request
localeCtx.setUserProfileLocale(getUserLocale(username));
Expand All @@ -90,9 +90,10 @@ public static LocaleContextInfo buildLocaleContextInfo(HttpServletRequest reques
localeCtx.setRemoteUser(username);
return localeCtx;
}

/**
* Helper method for setters invocation on {@link LocaleContextInfo} object
*
* @param userId
* @return a built {@link LocaleContextInfo} object
*/
Expand All @@ -101,12 +102,10 @@ public static LocaleContextInfo buildLocaleContextInfo(String userId) {
localeCtx.setSupportedLocales(getSupportedLocales());
localeCtx.setUserProfileLocale(getUserLocale(userId));
localeCtx.setRemoteUser(userId);
localeCtx.setPortalLocale(getPortalLocale());
return localeCtx;
}

/**
*
* @param request
* @return
*/
Expand All @@ -115,51 +114,55 @@ public static List<Locale> getCookieLocales(HttpServletRequest request) {
if (cookies != null) {
for (Cookie cookie : cookies) {
if (LOCALE_COOKIE.equals(cookie.getName())) {
List<Locale> locales = new ArrayList<Locale>();
List<Locale> locales = new ArrayList<>();
locales.add(LocaleContextInfo.getLocale(cookie.getValue()));
return locales;
}
}
}
return Collections.emptyList();
}

/**
* Get current session locale
*
* @param request
* @return
*/
private static Locale getSessionLocale(HttpServletRequest request) {
return getLocaleFromSession(request, LOCALE_SESSION_ATTR);
}

/**
* Get previous session locale
*
* @param request
* @return
*/
private static Locale getPreviousLocale(HttpServletRequest request) {
return getLocaleFromSession(request, PREV_LOCALE_SESSION_ATTR);
}

/**
* Helper method to retrieve supportedLocales from LocaleConfigService
*
* @return supportedLocales
*/
public static Set<Locale> getSupportedLocales() {
LocaleConfigService localeConfigService = ExoContainerContext.getCurrentContainer()
.getComponentInstanceOfType(LocaleConfigService.class);
Set<Locale> supportedLocales = new HashSet<>();
if (localeConfigService != null) {
for (LocaleConfig lc : localeConfigService.getLocalConfigs()) {
supportedLocales.add(lc.getLocale());
public static Set<Locale> getSupportedLocales() {
if (SUPPORTED_LOCALES.isEmpty()) {
LocaleConfigService localeConfigService = ExoContainerContext.getService(LocaleConfigService.class);
if (localeConfigService != null) {
for (LocaleConfig lc : localeConfigService.getLocalConfigs()) {
SUPPORTED_LOCALES.add(lc.getLocale());
}
}
}
return supportedLocales;
return SUPPORTED_LOCALES;
}

/**
* Get session locale
*
* @param request
* @param attrName
* @return
Expand All @@ -171,19 +174,20 @@ private static Locale getLocaleFromSession(HttpServletRequest request, String at
lang = (String) session.getAttribute(attrName);
return (lang != null) ? LocaleContextInfo.getLocale(lang) : null;
}

/**
* Helper method to retrieve user locale from UserProfile
*
* @param userId
* @return user locale
*/
private static Locale getUserLocale(String userId) {
String lang = "";
UserProfile profile = null;
//
if(userId != null) {
if (userId != null) {
OrganizationService organizationService = ExoContainerContext.getCurrentContainer()
.getComponentInstanceOfType(OrganizationService.class);
.getComponentInstanceOfType(OrganizationService.class);
// get user profile
beginContext(organizationService);
try {
Expand All @@ -194,7 +198,7 @@ private static Locale getUserLocale(String userId) {
endContext(organizationService);
}
// fetch profile lang
if(profile != null) {
if (profile != null) {
lang = profile.getAttribute(Constants.USER_LANGUAGE);
}
if (lang != null && lang.trim().length() > 0) {
Expand All @@ -203,47 +207,27 @@ private static Locale getUserLocale(String userId) {
}
return null;
}

/**
* Helper method to get portal locale from portal config
* @return return portalLocale, if not set then return the JVM Locale
*/
private static Locale getPortalLocale() {
String lang = "";
//
ExoContainer currentContainer = ExoContainerContext.getCurrentContainer();
if (currentContainer instanceof RootContainer) {
currentContainer = PortalContainer.getInstance();
}
UserPortalConfigService userPortalConfigService = currentContainer.getComponentInstanceOfType(UserPortalConfigService.class);
if(userPortalConfigService != null) {
PortalConfig config = userPortalConfigService.getMetaPortalConfig();
if (config != null) {
lang = config.getLocale();
}
}
// return portal Locale, if not set then return the JVM Locale
return (lang != null && lang.trim().length() > 0) ? LocaleUtils.toLocale(lang) : Locale.getDefault();
}


/**
* Begin request life cycle for OrganizationService
*
* @param orgService
*/
private static void beginContext(OrganizationService orgService) {
if (orgService instanceof ComponentRequestLifecycle) {
RequestLifeCycle.begin((ComponentRequestLifecycle) orgService);
}
}

/**
* End RequestLifeCycle for OrganizationService
*
* @param orgService
*/
private static void endContext(OrganizationService orgService) {
if (orgService instanceof ComponentRequestLifecycle) {
RequestLifeCycle.end();
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,77 +22,89 @@

/**
* A class that group available Spring integrations made for Meeds Portal and
* Meeds Kernel applications.
*
* Those integrations aren't imported automatically to let each addon choose
* which Beans and integrations to adopt which is convinient to not force initialise
* not needed Beans in certain contexts
* Meeds Kernel applications. Those integrations aren't imported automatically
* to let each addon choose which Beans and integrations to adopt which is
* convinient to not force initialise not needed Beans in certain contexts
*/
public class AvailableIntegration {

private AvailableIntegration() {}
private AvailableIntegration() {
}

/**
* Used to bring Kernel components into Spring Context and vice versa
* add Spring {@link Service} layer Beans into Kernel and other Spring contexts
* to share the service layer
* Used to bring Kernel components into Spring Context and vice versa add
* Spring {@link Service} layer Beans into Kernel and other Spring contexts to
* share the service layer
*/
public static final String KERNEL_MODULE = "io.meeds.spring.kernel";

/**
* see {@link AvailableIntegration#KERNEL_MODULE}
*
* Use in Test Scope only
* see {@link AvailableIntegration#KERNEL_MODULE} Use in Test Scope only
*/
public static final String KERNEL_TEST_MODULE = "io.meeds.spring.kernel.test";

/**
* Used when JPA entities are needed to be managed by Spring Data JPA and allows
* to reuse JPA PersistenceUnit defined globally in Kernel.
* Used when JPA entities are needed to be managed by Spring Data JPA and
* allows to reuse JPA PersistenceUnit defined globally in Kernel.
*/
public static final String JPA_MODULE = "io.meeds.spring.jpa";

/**
* Has to be used when Liquibase configuration and annotation are needed in an addon,
* otherwise, the parameter 'exclude = LiquibaseAutoConfiguration.class' has to be added in
* SpringBootApplication annotation to disable auto configuration of liquibase.
* Has to be used when Liquibase configuration and annotation are needed in an
* addon, otherwise, the parameter 'exclude =
* LiquibaseAutoConfiguration.class' has to be added in SpringBootApplication
* annotation to disable auto configuration of liquibase.
*/
public static final String LIQUIBASE_MODULE = "io.meeds.spring.liquibase";

/**
* Used to inject Meeds Portal Authentication and Authorization contexts to apply security
* on Spring REST/Controller endpoints. At the same time, this will allow to inject
* ConversationState.setCurrent as made in regular Portal REST endpoints.
* Used to inject Meeds Portal Authentication and Authorization contexts to
* apply security on Spring REST/Controller endpoints. At the same time, this
* will allow to inject ConversationState.setCurrent as made in regular Portal
* REST endpoints.
*/
public static final String WEB_MODULE = "io.meeds.spring.web";

/**
* Used to inject Meeds Portal Authentication and Authorization contexts to
* apply security on Spring REST/Controller endpoints. At the same time, this
* will allow to inject ConversationState.setCurrent as made in regular Portal
* REST endpoints.
*
* @deprecated use WEB_MODULE instead to integrate all web integrations
*/
@Deprecated(forRemoval = true)
public static final String WEB_SECURITY_MODULE = "io.meeds.spring.web.security";

/**
* Used to start and end a transaction at each Spring REST/Controller call, same as used
* to be in regular Portal REST endpoints.
* Used to start and end a transaction at each Spring REST/Controller call,
* same as used to be in regular Portal REST endpoints.
*
* @deprecated use WEB_MODULE instead
*/
@Deprecated(forRemoval = true)
public static final String WEB_TRANSACTION_MODULE = "io.meeds.spring.web.transaction";

/**
* Shortcut to list all available Meeds Portal and Kernel integration modules with Spring
* Shortcut to list all available Meeds Portal and Kernel integration modules
* with Spring
*/
public static final String[] ALL_MODULES = { // NOSONAR
KERNEL_MODULE,
JPA_MODULE,
LIQUIBASE_MODULE,
WEB_SECURITY_MODULE,
WEB_TRANSACTION_MODULE
public static final String[] ALL_MODULES = { // NOSONAR
KERNEL_MODULE,
JPA_MODULE,
LIQUIBASE_MODULE,
WEB_MODULE
};

/**
* Shortcut to list all available Meeds Portal and Kernel integration modules with Spring
* in Test scope
* Shortcut to list all available Meeds Portal and Kernel integration modules
* with Spring in Test scope
*/
public static final String[] ALL_TEST_MODULES = { // NOSONAR
KERNEL_TEST_MODULE,
JPA_MODULE,
LIQUIBASE_MODULE,
WEB_SECURITY_MODULE,
WEB_TRANSACTION_MODULE
public static final String[] ALL_TEST_MODULES = { // NOSONAR
KERNEL_TEST_MODULE,
JPA_MODULE,
LIQUIBASE_MODULE,
WEB_MODULE
};

}
Loading

0 comments on commit da21042

Please sign in to comment.