001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.i18n; 018 019import java.util.Locale; 020import java.util.TimeZone; 021 022import javax.servlet.http.HttpServletRequest; 023import javax.servlet.http.HttpServletResponse; 024 025import org.springframework.context.i18n.LocaleContext; 026import org.springframework.context.i18n.TimeZoneAwareLocaleContext; 027import org.springframework.lang.Nullable; 028import org.springframework.web.util.WebUtils; 029 030/** 031 * {@link org.springframework.web.servlet.LocaleResolver} implementation that 032 * uses a locale attribute in the user's session in case of a custom setting, 033 * with a fallback to the specified default locale or the request's 034 * accept-header locale. 035 * 036 * <p>This is most appropriate if the application needs user sessions anyway, 037 * i.e. when the {@code HttpSession} does not have to be created just for storing 038 * the user's locale. The session may optionally contain an associated time zone 039 * attribute as well; alternatively, you may specify a default time zone. 040 * 041 * <p>Custom controllers can override the user's locale and time zone by calling 042 * {@code #setLocale(Context)} on the resolver, e.g. responding to a locale change 043 * request. As a more convenient alternative, consider using 044 * {@link org.springframework.web.servlet.support.RequestContext#changeLocale}. 045 * 046 * <p>In contrast to {@link CookieLocaleResolver}, this strategy stores locally 047 * chosen locale settings in the Servlet container's {@code HttpSession}. As a 048 * consequence, those settings are just temporary for each session and therefore 049 * lost when each session terminates. 050 * 051 * <p>Note that there is no direct relationship with external session management 052 * mechanisms such as the "Spring Session" project. This {@code LocaleResolver} 053 * will simply evaluate and modify corresponding {@code HttpSession} attributes 054 * against the current {@code HttpServletRequest}. 055 * 056 * @author Juergen Hoeller 057 * @since 27.02.2003 058 * @see #setDefaultLocale 059 * @see #setDefaultTimeZone 060 */ 061public class SessionLocaleResolver extends AbstractLocaleContextResolver { 062 063 /** 064 * Name of the session attribute that holds the Locale. 065 * Only used internally by this implementation. 066 * <p>Use {@code RequestContext(Utils).getLocale()} 067 * to retrieve the current locale in controllers or views. 068 * @see org.springframework.web.servlet.support.RequestContext#getLocale 069 * @see org.springframework.web.servlet.support.RequestContextUtils#getLocale 070 */ 071 public static final String LOCALE_SESSION_ATTRIBUTE_NAME = SessionLocaleResolver.class.getName() + ".LOCALE"; 072 073 /** 074 * Name of the session attribute that holds the TimeZone. 075 * Only used internally by this implementation. 076 * <p>Use {@code RequestContext(Utils).getTimeZone()} 077 * to retrieve the current time zone in controllers or views. 078 * @see org.springframework.web.servlet.support.RequestContext#getTimeZone 079 * @see org.springframework.web.servlet.support.RequestContextUtils#getTimeZone 080 */ 081 public static final String TIME_ZONE_SESSION_ATTRIBUTE_NAME = SessionLocaleResolver.class.getName() + ".TIME_ZONE"; 082 083 084 private String localeAttributeName = LOCALE_SESSION_ATTRIBUTE_NAME; 085 086 private String timeZoneAttributeName = TIME_ZONE_SESSION_ATTRIBUTE_NAME; 087 088 089 /** 090 * Specify the name of the corresponding attribute in the {@code HttpSession}, 091 * holding the current {@link Locale} value. 092 * <p>The default is an internal {@link #LOCALE_SESSION_ATTRIBUTE_NAME}. 093 * @since 4.3.8 094 */ 095 public void setLocaleAttributeName(String localeAttributeName) { 096 this.localeAttributeName = localeAttributeName; 097 } 098 099 /** 100 * Specify the name of the corresponding attribute in the {@code HttpSession}, 101 * holding the current {@link TimeZone} value. 102 * <p>The default is an internal {@link #TIME_ZONE_SESSION_ATTRIBUTE_NAME}. 103 * @since 4.3.8 104 */ 105 public void setTimeZoneAttributeName(String timeZoneAttributeName) { 106 this.timeZoneAttributeName = timeZoneAttributeName; 107 } 108 109 110 @Override 111 public Locale resolveLocale(HttpServletRequest request) { 112 Locale locale = (Locale) WebUtils.getSessionAttribute(request, this.localeAttributeName); 113 if (locale == null) { 114 locale = determineDefaultLocale(request); 115 } 116 return locale; 117 } 118 119 @Override 120 public LocaleContext resolveLocaleContext(final HttpServletRequest request) { 121 return new TimeZoneAwareLocaleContext() { 122 @Override 123 public Locale getLocale() { 124 Locale locale = (Locale) WebUtils.getSessionAttribute(request, localeAttributeName); 125 if (locale == null) { 126 locale = determineDefaultLocale(request); 127 } 128 return locale; 129 } 130 @Override 131 @Nullable 132 public TimeZone getTimeZone() { 133 TimeZone timeZone = (TimeZone) WebUtils.getSessionAttribute(request, timeZoneAttributeName); 134 if (timeZone == null) { 135 timeZone = determineDefaultTimeZone(request); 136 } 137 return timeZone; 138 } 139 }; 140 } 141 142 @Override 143 public void setLocaleContext(HttpServletRequest request, @Nullable HttpServletResponse response, 144 @Nullable LocaleContext localeContext) { 145 146 Locale locale = null; 147 TimeZone timeZone = null; 148 if (localeContext != null) { 149 locale = localeContext.getLocale(); 150 if (localeContext instanceof TimeZoneAwareLocaleContext) { 151 timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone(); 152 } 153 } 154 WebUtils.setSessionAttribute(request, this.localeAttributeName, locale); 155 WebUtils.setSessionAttribute(request, this.timeZoneAttributeName, timeZone); 156 } 157 158 159 /** 160 * Determine the default locale for the given request, 161 * Called if no Locale session attribute has been found. 162 * <p>The default implementation returns the specified default locale, 163 * if any, else falls back to the request's accept-header locale. 164 * @param request the request to resolve the locale for 165 * @return the default locale (never {@code null}) 166 * @see #setDefaultLocale 167 * @see javax.servlet.http.HttpServletRequest#getLocale() 168 */ 169 protected Locale determineDefaultLocale(HttpServletRequest request) { 170 Locale defaultLocale = getDefaultLocale(); 171 if (defaultLocale == null) { 172 defaultLocale = request.getLocale(); 173 } 174 return defaultLocale; 175 } 176 177 /** 178 * Determine the default time zone for the given request, 179 * Called if no TimeZone session attribute has been found. 180 * <p>The default implementation returns the specified default time zone, 181 * if any, or {@code null} otherwise. 182 * @param request the request to resolve the time zone for 183 * @return the default time zone (or {@code null} if none defined) 184 * @see #setDefaultTimeZone 185 */ 186 @Nullable 187 protected TimeZone determineDefaultTimeZone(HttpServletRequest request) { 188 return getDefaultTimeZone(); 189 } 190 191}