All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aspectran.web.support.i18n.locale.CookieLocaleResolver Maven / Gradle / Ivy

There is a newer version: 8.1.5
Show newest version
/*
 * Copyright (c) 2008-2024 The Aspectran Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.aspectran.web.support.i18n.locale;

import com.aspectran.core.activity.Translet;
import com.aspectran.core.support.i18n.locale.AbstractLocaleResolver;
import com.aspectran.core.support.i18n.locale.LocaleResolver;
import com.aspectran.utils.LocaleUtils;
import com.aspectran.utils.annotation.jsr305.NonNull;
import com.aspectran.utils.annotation.jsr305.Nullable;
import com.aspectran.utils.logging.Logger;
import com.aspectran.utils.logging.LoggerFactory;
import com.aspectran.web.support.util.CookieGenerator;
import com.aspectran.web.support.util.WebUtils;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.util.Locale;
import java.util.TimeZone;

/**
 * 

This class is a clone of org.springframework.web.servlet.i18n.CookieLocaleResolver

* * {@link LocaleResolver} implementation that uses a cookie sent back to the user * in case of a custom setting, with a fallback to the specified default locale * or the request's accept-header locale. * *

This is particularly useful for stateless applications without user sessions. * The cookie may optionally contain an associated time zone value as well; * alternatively, you may specify a default time zone.

*/ public class CookieLocaleResolver extends AbstractLocaleResolver { private static final Logger logger = LoggerFactory.getLogger(CookieLocaleResolver.class); private static final String LOCALE_COOKIE_NAME = CookieLocaleResolver.class.getName() + ".LOCALE"; private static final String TIME_ZONE_COOKIE_NAME = CookieLocaleResolver.class.getName() + ".TIME_ZONE"; private volatile CookieGenerator localeCookieGenerator; private volatile CookieGenerator timeZoneCookieGenerator; private boolean languageTagCompliant = true; private boolean rejectInvalidCookies = true; /** * Create a new instance of the {@link CookieLocaleResolver} class. */ public CookieLocaleResolver() { } public void setLocaleCookieGenerator(CookieGenerator localeCookieGenerator) { this.localeCookieGenerator = localeCookieGenerator; } public CookieGenerator getLocaleCookieGenerator() { if (localeCookieGenerator == null) { localeCookieGenerator = new CookieGenerator(); localeCookieGenerator.setCookieName(LOCALE_COOKIE_NAME); } return localeCookieGenerator; } public void setTimeZoneCookieGenerator(CookieGenerator timeZoneCookieGenerator) { this.timeZoneCookieGenerator = timeZoneCookieGenerator; } public CookieGenerator getTimeZoneCookieGenerator() { if (timeZoneCookieGenerator == null) { timeZoneCookieGenerator = new CookieGenerator(); timeZoneCookieGenerator.setCookieName(TIME_ZONE_COOKIE_NAME); } return timeZoneCookieGenerator; } /** * Use the given domain for cookies. * The cookie is only visible to servers in this domain. * * @see jakarta.servlet.http.Cookie#setDomain */ public void setCookieDomain(String cookieDomain) { getLocaleCookieGenerator().setCookieDomain(cookieDomain); getTimeZoneCookieGenerator().setCookieDomain(cookieDomain); } /** * Use the given path for cookies. * The cookie is only visible to URLs in this path and below. * * @see jakarta.servlet.http.Cookie#setPath */ public void setCookiePath(String cookiePath) { getLocaleCookieGenerator().setCookiePath(cookiePath); getTimeZoneCookieGenerator().setCookiePath(cookiePath); } /** * Use the given maximum age (in seconds) for cookies. * Useful special value: -1 ... not persistent, deleted when client shuts down. *

Default is no specific maximum age at all, using the Servlet container's * default.

* * @see jakarta.servlet.http.Cookie#setMaxAge */ public void setCookieMaxAge(Integer cookieMaxAge) { getLocaleCookieGenerator().setCookieMaxAge(cookieMaxAge); getTimeZoneCookieGenerator().setCookieMaxAge(cookieMaxAge); } /** * Set whether the cookie should only be sent using a secure protocol, * such as HTTPS (SSL). This is an indication to the receiving browser, * not processed by the HTTP server itself. *

Default is "false".

* * @see jakarta.servlet.http.Cookie#setSecure */ public void setCookieSecure(boolean cookieSecure) { getLocaleCookieGenerator().setCookieSecure(cookieSecure); getTimeZoneCookieGenerator().setCookieSecure(cookieSecure); } /** * Set whether the cookie is supposed to be marked with the "HttpOnly" attribute. *

Default is "false".

* * @see jakarta.servlet.http.Cookie#setHttpOnly */ public void setCookieHttpOnly(boolean cookieHttpOnly) { getLocaleCookieGenerator().setCookieHttpOnly(cookieHttpOnly); getTimeZoneCookieGenerator().setCookieHttpOnly(cookieHttpOnly); } /** * Specify whether this resolver's cookies should be compliant with BCP 47 * language tags instead of Java's legacy locale specification format. *

The default is {@code true}, as of 5.1. Switch this to {@code false} * for rendering Java's legacy locale specification format. For parsing, * this resolver leniently accepts the legacy {@link Locale#toString} * format as well as BCP 47 language tags in any case.

* * @see #parseLocaleValue(String) * @see #toLocaleValue(Locale) * @see Locale#forLanguageTag(String) * @see Locale#toLanguageTag() */ public void setLanguageTagCompliant(boolean languageTagCompliant) { this.languageTagCompliant = languageTagCompliant; } /** * Return whether this resolver's cookies should be compliant with BCP 47 * language tags instead of Java's legacy locale specification format. */ public boolean isLanguageTagCompliant() { return this.languageTagCompliant; } /** * Specify whether to reject cookies with invalid content (e.g. invalid format). *

The default is {@code true}. Turn this off for lenient handling of parse * failures, falling back to the default locale and time zone in such a case.

* * @see #setDefaultLocale * @see #setDefaultTimeZone * @see #determineDefaultLocale * @see #determineDefaultTimeZone */ public void setRejectInvalidCookies(boolean rejectInvalidCookies) { this.rejectInvalidCookies = rejectInvalidCookies; } /** * Return whether to reject cookies with invalid content (e.g. invalid format). */ public boolean isRejectInvalidCookies() { return this.rejectInvalidCookies; } @Override public Locale resolveLocale(@NonNull Translet translet) { Locale locale = parseLocaleCookie(translet); if (locale != null) { translet.getRequestAdapter().setLocale(locale); return locale; } return determineDefaultLocale(translet); } @Override public TimeZone resolveTimeZone(@NonNull Translet translet) { TimeZone timeZone = parseTimeZoneCookie(translet); if (timeZone != null) { translet.getRequestAdapter().setTimeZone(timeZone); return timeZone; } return determineDefaultTimeZone(translet); } @Override public void setLocale(@NonNull Translet translet, Locale locale) { translet.getRequestAdapter().setLocale(locale); HttpServletResponse response = translet.getResponseAdapter().getAdaptee(); getLocaleCookieGenerator().addCookie(response, (locale != null ? toLocaleValue(locale) : "")); } @Override public void setTimeZone(@NonNull Translet translet, TimeZone timeZone) { translet.getRequestAdapter().setTimeZone(timeZone); HttpServletResponse response = translet.getResponseAdapter().getAdaptee(); getTimeZoneCookieGenerator().addCookie(response, (timeZone != null ? timeZone.getID() : "")); } private Locale parseLocaleCookie(@NonNull Translet translet) { Locale locale = null; String cookieName = getLocaleCookieGenerator().getCookieName(); HttpServletRequest request = translet.getRequestAdapter().getAdaptee(); Cookie cookie = WebUtils.getCookie(request, cookieName); if (cookie != null) { String value = cookie.getValue(); try { locale = parseLocaleValue(value); } catch (IllegalArgumentException ex) { if (isRejectInvalidCookies() && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) { throw new IllegalStateException("Encountered invalid locale cookie '" + cookieName + "': [" + value + "] due to: " + ex.getMessage()); } else { // Lenient handling (e.g. error dispatch): ignore locale/timezone parse exceptions if (logger.isDebugEnabled()) { logger.debug("Ignoring invalid locale cookie '" + cookieName + "': [" + value + "] due to: " + ex.getMessage()); } } } if (locale != null && logger.isTraceEnabled()) { logger.trace("Parsed cookie value [" + cookie.getValue() + "] into locale '" + locale + "'"); } } return locale; } private TimeZone parseTimeZoneCookie(@NonNull Translet translet) { TimeZone timeZone = null; String cookieName = getTimeZoneCookieGenerator().getCookieName(); HttpServletRequest request = translet.getRequestAdapter().getAdaptee(); Cookie cookie = WebUtils.getCookie(request, cookieName); if (cookie != null) { String value = cookie.getValue(); try { timeZone = LocaleUtils.parseTimeZoneString(value); } catch (IllegalArgumentException ex) { if (isRejectInvalidCookies() && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) { throw new IllegalStateException("Encountered invalid time zone cookie '" + cookieName + "': [" + value + "] due to: " + ex.getMessage()); } else { // Lenient handling (e.g. error dispatch): ignore locale/timezone parse exceptions if (logger.isDebugEnabled()) { logger.debug("Ignoring invalid time zone cookie '" + cookieName + "': [" + value + "] due to: " + ex.getMessage()); } } } if (timeZone != null && logger.isTraceEnabled()) { logger.trace("Parsed cookie value [" + cookie.getValue() + "] into time zone '" + timeZone.getID() + "'"); } } return timeZone; } /** * Parse the given locale value coming from an incoming cookie. *

The default implementation calls {@link LocaleUtils#parseLocale(String)}, * accepting the {@link Locale#toString} format as well as BCP 47 language tags.

* @param localeValue the locale value to parse * @return the corresponding {@code Locale} instance * @see LocaleUtils#parseLocale(String) */ @Nullable protected Locale parseLocaleValue(String localeValue) { return LocaleUtils.parseLocale(localeValue); } /** * Render the given locale as a text value for inclusion in a cookie. *

The default implementation calls {@link Locale#toString()} * or JDK 7's {@link Locale#toLanguageTag()}, depending on the * {@link #setLanguageTagCompliant "languageTagCompliant"} configuration property. * @param locale the locale to stringify * @return a String representation for the given locale * @see #isLanguageTagCompliant() */ protected String toLocaleValue(Locale locale) { return (isLanguageTagCompliant() ? locale.toLanguageTag() : locale.toString()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy