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.support; 018 019import java.util.Locale; 020import java.util.ResourceBundle; 021import java.util.TimeZone; 022 023import javax.servlet.ServletContext; 024import javax.servlet.http.HttpServletRequest; 025import javax.servlet.http.HttpSession; 026import javax.servlet.jsp.jstl.core.Config; 027import javax.servlet.jsp.jstl.fmt.LocalizationContext; 028 029import org.springframework.context.MessageSource; 030import org.springframework.context.support.MessageSourceResourceBundle; 031import org.springframework.context.support.ResourceBundleMessageSource; 032import org.springframework.lang.Nullable; 033 034/** 035 * Helper class for preparing JSTL views, 036 * in particular for exposing a JSTL localization context. 037 * 038 * @author Juergen Hoeller 039 * @since 20.08.2003 040 */ 041public abstract class JstlUtils { 042 043 /** 044 * Checks JSTL's "javax.servlet.jsp.jstl.fmt.localizationContext" 045 * context-param and creates a corresponding child message source, 046 * with the provided Spring-defined MessageSource as parent. 047 * @param servletContext the ServletContext we're running in 048 * (to check JSTL-related context-params in {@code web.xml}) 049 * @param messageSource the MessageSource to expose, typically 050 * the ApplicationContext of the current DispatcherServlet 051 * @return the MessageSource to expose to JSTL; first checking the 052 * JSTL-defined bundle, then the Spring-defined MessageSource 053 * @see org.springframework.context.ApplicationContext 054 */ 055 public static MessageSource getJstlAwareMessageSource( 056 @Nullable ServletContext servletContext, MessageSource messageSource) { 057 058 if (servletContext != null) { 059 String jstlInitParam = servletContext.getInitParameter(Config.FMT_LOCALIZATION_CONTEXT); 060 if (jstlInitParam != null) { 061 // Create a ResourceBundleMessageSource for the specified resource bundle 062 // basename in the JSTL context-param in web.xml, wiring it with the given 063 // Spring-defined MessageSource as parent. 064 ResourceBundleMessageSource jstlBundleWrapper = new ResourceBundleMessageSource(); 065 jstlBundleWrapper.setBasename(jstlInitParam); 066 jstlBundleWrapper.setParentMessageSource(messageSource); 067 return jstlBundleWrapper; 068 } 069 } 070 return messageSource; 071 } 072 073 /** 074 * Exposes JSTL-specific request attributes specifying locale 075 * and resource bundle for JSTL's formatting and message tags, 076 * using Spring's locale and MessageSource. 077 * @param request the current HTTP request 078 * @param messageSource the MessageSource to expose, 079 * typically the current ApplicationContext (may be {@code null}) 080 * @see #exposeLocalizationContext(RequestContext) 081 */ 082 public static void exposeLocalizationContext(HttpServletRequest request, @Nullable MessageSource messageSource) { 083 Locale jstlLocale = RequestContextUtils.getLocale(request); 084 Config.set(request, Config.FMT_LOCALE, jstlLocale); 085 TimeZone timeZone = RequestContextUtils.getTimeZone(request); 086 if (timeZone != null) { 087 Config.set(request, Config.FMT_TIME_ZONE, timeZone); 088 } 089 if (messageSource != null) { 090 LocalizationContext jstlContext = new SpringLocalizationContext(messageSource, request); 091 Config.set(request, Config.FMT_LOCALIZATION_CONTEXT, jstlContext); 092 } 093 } 094 095 /** 096 * Exposes JSTL-specific request attributes specifying locale 097 * and resource bundle for JSTL's formatting and message tags, 098 * using Spring's locale and MessageSource. 099 * @param requestContext the context for the current HTTP request, 100 * including the ApplicationContext to expose as MessageSource 101 */ 102 public static void exposeLocalizationContext(RequestContext requestContext) { 103 Config.set(requestContext.getRequest(), Config.FMT_LOCALE, requestContext.getLocale()); 104 TimeZone timeZone = requestContext.getTimeZone(); 105 if (timeZone != null) { 106 Config.set(requestContext.getRequest(), Config.FMT_TIME_ZONE, timeZone); 107 } 108 MessageSource messageSource = getJstlAwareMessageSource( 109 requestContext.getServletContext(), requestContext.getMessageSource()); 110 LocalizationContext jstlContext = new SpringLocalizationContext(messageSource, requestContext.getRequest()); 111 Config.set(requestContext.getRequest(), Config.FMT_LOCALIZATION_CONTEXT, jstlContext); 112 } 113 114 115 /** 116 * Spring-specific LocalizationContext adapter that merges session-scoped 117 * JSTL LocalizationContext/Locale attributes with the local Spring request context. 118 */ 119 private static class SpringLocalizationContext extends LocalizationContext { 120 121 private final MessageSource messageSource; 122 123 private final HttpServletRequest request; 124 125 public SpringLocalizationContext(MessageSource messageSource, HttpServletRequest request) { 126 this.messageSource = messageSource; 127 this.request = request; 128 } 129 130 @Override 131 public ResourceBundle getResourceBundle() { 132 HttpSession session = this.request.getSession(false); 133 if (session != null) { 134 Object lcObject = Config.get(session, Config.FMT_LOCALIZATION_CONTEXT); 135 if (lcObject instanceof LocalizationContext) { 136 ResourceBundle lcBundle = ((LocalizationContext) lcObject).getResourceBundle(); 137 return new MessageSourceResourceBundle(this.messageSource, getLocale(), lcBundle); 138 } 139 } 140 return new MessageSourceResourceBundle(this.messageSource, getLocale()); 141 } 142 143 @Override 144 public Locale getLocale() { 145 HttpSession session = this.request.getSession(false); 146 if (session != null) { 147 Object localeObject = Config.get(session, Config.FMT_LOCALE); 148 if (localeObject instanceof Locale) { 149 return (Locale) localeObject; 150 } 151 } 152 return RequestContextUtils.getLocale(this.request); 153 } 154 } 155 156}