org.springframework.web.filter.TimeZoneAwareLocaleChangeFilter Maven / Gradle / Ivy
package org.springframework.web.filter;
import java.io.IOException;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.i18n.SimpleTimeZoneAwareLocaleContext;
import org.springframework.context.i18n.TimeZoneAwareLocaleContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.LocaleContextResolver;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.support.RequestContextUtils;
/**
* @see RequestContextUtils#getLocaleResolver(HttpServletRequest)
* @see RequestContextUtils#getLocale(HttpServletRequest)
* @see LocaleContextHolder#setLocaleContext(org.springframework.context.i18n.LocaleContext)
* @see org.springframework.beans.propertyeditors.LocaleEditor
* @see org.springframework.context.i18n.SimpleTimeZoneAwareLocaleContext
* @see org.springframework.web.servlet.i18n.LocaleChangeInterceptor
* @see org.springframework.web.servlet.support.RequestContextUtils#getLocale(HttpServletRequest)
* @see org.springframework.session.web.http.SessionRepositoryFilter#DEFAULT_ORDER
* @see org.springframework.boot.web.servlet.FilterRegistrationBean#REQUEST_WRAPPER_FILTER_MAX_ORDER
*/
public class TimeZoneAwareLocaleChangeFilter extends OncePerRequestFilter {
protected final Log logger = LogFactory.getLog(getClass());
private static final String DEFAULT_LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
private static final String DEFAULT_TIME_ZONE_PARAM_NAME = "timeZone";
private static final String DEFAULT_LOCALE_PARAM_NAME = "locale";
private static final String CONTENT_TIME_ZONE = "Content-Time-Zone";
private String localeResolverBeanName = DEFAULT_LOCALE_RESOLVER_BEAN_NAME;
private String timeZoneParamName = DEFAULT_TIME_ZONE_PARAM_NAME;
private String localeParamName = DEFAULT_LOCALE_PARAM_NAME;
private boolean header;
private boolean createSessionAllowed;
private List httpMethod = new ArrayList();
private List locale = Arrays.asList(Locale.getDefault());
private List timeZone = Arrays.asList(TimeZone.getDefault());
/**
* @see org.springframework.security.web.savedrequest.HttpSessionRequestCache#saveRequest(HttpServletRequest,
* HttpServletResponse)
* @see org.springframework.web.servlet.i18n.SessionLocaleResolver
* @see org.springframework.web.util.WebUtils#getSessionId(HttpServletRequest)
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String newLocale = request.getParameter(getLocaleParamName());
String newTimeZone = request.getParameter(getTimeZoneParamName());
Locale previousLocale = LocaleContextHolder.getLocale();
TimeZone previousTimeZone = LocaleContextHolder.getTimeZone();
LocaleResolver localeResolver = lookupLocaleResolver(request);
if (localeResolver instanceof LocaleContextResolver) {
LocaleContext localeContext = ((LocaleContextResolver) localeResolver).resolveLocaleContext(request);
if (localeContext instanceof TimeZoneAwareLocaleContext) {
previousTimeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone();
}
previousLocale = localeContext.getLocale();
}
else if (localeResolver instanceof LocaleResolver) {
previousLocale = localeResolver.resolveLocale(request);
}
Locale locale = determineLocale(this.locale, previousLocale);
TimeZone timeZone = determineTimeZone(this.timeZone, previousTimeZone);
if (checkHttpMethod(request.getMethod())) {
if (StringUtils.hasText(newLocale)) {
locale = determineLocale(this.locale, parseLocaleString(newLocale));
if (logger.isDebugEnabled()) {
logger.debug("Input Locale [" + getLocaleParamName() + "]([" + newLocale + "]): " + locale);
}
}
if (StringUtils.hasText(newTimeZone)) {
timeZone = determineTimeZone(this.timeZone, parseTimeZoneString(newTimeZone));
if (logger.isDebugEnabled()) {
logger.debug("Input TimeZone [" + getTimeZoneParamName() + "]([" + newTimeZone + "]): " + timeZone);
}
}
}
boolean changeLocale = !locale.equals(previousLocale);
boolean changeTimeZone = !timeZone.equals(previousTimeZone);
if (changeLocale || changeTimeZone) {
if (changeLocale && logger.isDebugEnabled()) {
logger.debug("Change Locale [" + locale + "] from [" + previousLocale + "]");
}
if (changeTimeZone && logger.isDebugEnabled()) {
logger.debug("Change TimeZone [" + timeZone.getID() + "] from [" + (previousTimeZone == null ? null : previousTimeZone.getID()) + "]");
}
if (localeResolver instanceof LocaleContextResolver) {
if (createSessionAllowed || request.getSession(false) != null) {
((LocaleContextResolver) localeResolver).setLocaleContext(request, response, new SimpleTimeZoneAwareLocaleContext(locale, timeZone));
}
}
else if (localeResolver instanceof LocaleResolver) {
localeResolver.setLocale(request, response, locale);
}
}
if (header) {
if (!response.containsHeader(HttpHeaders.CONTENT_LANGUAGE)) {
response.addHeader(HttpHeaders.CONTENT_LANGUAGE, locale.toLanguageTag());
}
if (!response.containsHeader(CONTENT_TIME_ZONE)) {
response.addHeader(CONTENT_TIME_ZONE, timeZone.getID());
}
}
filterChain.doFilter(request, response);
}
private boolean checkHttpMethod(String currentMethod) {
List configuredMethods = getHttpMethod();
if (CollectionUtils.isEmpty(configuredMethods)) {
return true;
}
if (StringUtils.hasText(currentMethod)) {
for (HttpMethod configuredMethod : configuredMethods) {
if (configuredMethod.matches(currentMethod.toUpperCase())) {
return true;
}
}
}
return false;
}
protected LocaleResolver lookupLocaleResolver(HttpServletRequest request) {
return lookupLocaleResolver();
}
protected LocaleResolver lookupLocaleResolver() {
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
String beanName = getLocaleResolverBeanName();
if (wac != null && wac.containsBean(beanName)) {
if (logger.isDebugEnabled()) {
logger.debug("Using LocaleResolver '" + beanName + "' for TimeZoneAwareLocaleChangeFilter");
}
return wac.getBean(beanName, LocaleResolver.class);
}
return null;
}
public Locale determineLocale(Collection locales, Locale locale) {
if (CollectionUtils.isEmpty(locales)) {
if (locale == null || Locale.ROOT.equals(locale)) {
locale = LocaleContextHolder.getLocale();
}
if (locale == null) {
locale = Locale.getDefault();
}
Assert.notNull(locale, "Not found 'Locale' from System");
return locale;
}
if (locale == null) {
return locales.iterator().next();
}
if (locales.contains(locale)) {
return locale;
}
for (Iterator iterator = locales.iterator(); iterator.hasNext();) {
Locale next = iterator.next();
if (locale.getLanguage().equals(next.getLanguage())) {
return next;
}
}
return locales.iterator().next();
}
/**
*
* TimeZone.getTimeZone("")
*
*/
public TimeZone determineTimeZone(Collection timeZones, TimeZone timeZone) {
if (CollectionUtils.isEmpty(timeZones)) {
if (timeZone == null || TimeZone.getTimeZone(ZoneOffset.UTC.normalized()).hasSameRules(timeZone)) {
timeZone = LocaleContextHolder.getTimeZone();
}
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}
Assert.notNull(timeZone, "Not found 'TimeZone' from System");
return timeZone;
}
if (timeZone == null) {
return timeZones.iterator().next();
}
if (timeZones.contains(timeZone)) {
return timeZone;
}
for (Iterator iterator = timeZones.iterator(); iterator.hasNext();) {
TimeZone next = iterator.next();
if (timeZone.hasSameRules(next)) {
return next;
}
}
return timeZones.iterator().next();
}
/**
* {@code localeString.replace('-', '_')}
*/
public Locale parseLocaleString(final String localeString) {
try {
Assert.hasText(localeString, "'localeString' must not be empty");
return StringUtils.parseLocaleString(localeString);
}
catch (IllegalArgumentException e) {
if (logger.isTraceEnabled()) {
logger.trace("Parse Locale fail... from " + localeString, e);
}
else if (logger.isWarnEnabled()) {
logger.warn("Parse Locale fail... from " + localeString + " (" + e.getMessage() + ")");
}
return null;
}
}
public TimeZone parseTimeZoneString(final String timeZoneString) {
try {
Assert.hasText(timeZoneString, "'timeZoneString' must not be empty");
return StringUtils.parseTimeZoneString(timeZoneString);
}
catch (IllegalArgumentException e) {
if (logger.isTraceEnabled()) {
logger.trace("Parse TimeZone fail... from " + timeZoneString, e);
}
else if (logger.isWarnEnabled()) {
logger.warn("Parse TimeZone fail... from " + timeZoneString + " (" + e.getMessage() + ")");
}
return null;
}
}
public String getLocaleResolverBeanName() {
return localeResolverBeanName;
}
public void setLocaleResolverBeanName(String localeResolverBeanName) {
this.localeResolverBeanName = localeResolverBeanName;
}
public String getTimeZoneParamName() {
return timeZoneParamName;
}
public void setTimeZoneParamName(String timeZoneParamName) {
this.timeZoneParamName = timeZoneParamName;
}
public String getLocaleParamName() {
return localeParamName;
}
public void setLocaleParamName(String localeParamName) {
this.localeParamName = localeParamName;
}
public List getHttpMethod() {
return httpMethod;
}
public void setHttpMethod(List httpMethod) {
this.httpMethod = httpMethod;
}
public List getLocale() {
return locale;
}
public void setLocale(List locale) {
this.locale = locale;
}
public List getTimeZone() {
return timeZone;
}
public void setTimeZone(List timeZone) {
this.timeZone = timeZone;
}
public boolean isHeader() {
return header;
}
public void setHeader(boolean header) {
this.header = header;
}
public boolean isCreateSessionAllowed() {
return createSessionAllowed;
}
public void setCreateSessionAllowed(boolean createSessionAllowed) {
this.createSessionAllowed = createSessionAllowed;
}
}