diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpEncodingAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpEncodingAutoConfiguration.java index 95930db476b..0e6e639b971 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpEncodingAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpEncodingAutoConfiguration.java @@ -20,11 +20,15 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.web.HttpEncodingProperties.Type; +import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; +import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.filter.OrderedCharacterEncodingFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; import org.springframework.web.filter.CharacterEncodingFilter; /** @@ -32,10 +36,12 @@ import org.springframework.web.filter.CharacterEncodingFilter; * in web applications. * * @author Stephane Nicoll + * @author Brian Clozel * @since 1.2.0 */ @Configuration @EnableConfigurationProperties(HttpEncodingProperties.class) +@ConditionalOnWebApplication @ConditionalOnClass(CharacterEncodingFilter.class) @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) public class HttpEncodingAutoConfiguration { @@ -56,4 +62,30 @@ public class HttpEncodingAutoConfiguration { return filter; } + @Bean + public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() { + return new LocaleCharsetMappingsCustomizer(this.properties); + } + + private static class LocaleCharsetMappingsCustomizer implements EmbeddedServletContainerCustomizer, Ordered { + + private final HttpEncodingProperties properties; + + LocaleCharsetMappingsCustomizer(HttpEncodingProperties properties) { + this.properties = properties; + } + + @Override + public void customize(ConfigurableEmbeddedServletContainer container) { + if (this.properties.getMapping() != null) { + container.setLocaleCharsetMappings(this.properties.getMapping()); + } + } + + @Override + public int getOrder() { + return 0; + } + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpEncodingProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpEncodingProperties.java index feb2db66df3..22563b78f05 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpEncodingProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/HttpEncodingProperties.java @@ -17,6 +17,8 @@ package org.springframework.boot.autoconfigure.web; import java.nio.charset.Charset; +import java.util.Locale; +import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -24,6 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * Configuration properties for http encoding. * * @author Stephane Nicoll + * @author Brian Clozel * @since 1.2.0 */ @ConfigurationProperties(prefix = "spring.http.encoding") @@ -53,6 +56,11 @@ public class HttpEncodingProperties { */ private Boolean forceResponse; + /** + * Locale to Encoding mapping. + */ + private Map mapping; + public Charset getCharset() { return this.charset; } @@ -85,6 +93,14 @@ public class HttpEncodingProperties { this.forceResponse = forceResponse; } + public Map getMapping() { + return this.mapping; + } + + public void setMapping(Map mapping) { + this.mapping = mapping; + } + boolean shouldForce(Type type) { Boolean force = (type == Type.REQUEST ? this.forceRequest : this.forceResponse); if (force == null) { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpEncodingAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpEncodingAutoConfigurationTests.java index 45bc254aefc..ac03ec2501c 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpEncodingAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/HttpEncodingAutoConfigurationTests.java @@ -16,8 +16,11 @@ package org.springframework.boot.autoconfigure.web; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; +import java.util.Locale; +import java.util.Map; import javax.servlet.Filter; @@ -27,13 +30,17 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; +import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor; +import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter; import org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.mock.web.MockServletContext; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.filter.HiddenHttpMethodFilter; @@ -49,7 +56,7 @@ public class HttpEncodingAutoConfigurationTests { @Rule public final ExpectedException thrown = ExpectedException.none(); - private AnnotationConfigApplicationContext context; + private AnnotationConfigWebApplicationContext context; @After public void close() { @@ -135,6 +142,31 @@ public class HttpEncodingAutoConfigurationTests { assertThat(beans.get(1)).isInstanceOf(HiddenHttpMethodFilter.class); } + @Test + public void noLocaleCharsetMapping() { + load(EmptyConfiguration.class); + Map beans = + this.context.getBeansOfType(EmbeddedServletContainerCustomizer.class); + assertThat(beans.size()).isEqualTo(1); + assertThat(this.context.getBean(MockEmbeddedServletContainerFactory.class) + .getLocaleCharsetMappings().size()).isEqualTo(0); + } + + @Test + public void customLocaleCharsetMappings() { + load(EmptyConfiguration.class, "spring.http.encoding.mapping.en:UTF-8", + "spring.http.encoding.mapping.fr_FR:UTF-8"); + Map beans = + this.context.getBeansOfType(EmbeddedServletContainerCustomizer.class); + assertThat(beans.size()).isEqualTo(1); + assertThat(this.context.getBean(MockEmbeddedServletContainerFactory.class) + .getLocaleCharsetMappings().size()).isEqualTo(2); + assertThat(this.context.getBean(MockEmbeddedServletContainerFactory.class) + .getLocaleCharsetMappings().get(Locale.ENGLISH)).isEqualTo(Charset.forName("UTF-8")); + assertThat(this.context.getBean(MockEmbeddedServletContainerFactory.class) + .getLocaleCharsetMappings().get(Locale.FRANCE)).isEqualTo(Charset.forName("UTF-8")); + } + private void assertCharacterEncodingFilter(CharacterEncodingFilter actual, String encoding, boolean forceRequestEncoding, boolean forceResponseEncoding) { @@ -147,12 +179,14 @@ public class HttpEncodingAutoConfigurationTests { this.context = doLoad(new Class[] { config }, environment); } - private AnnotationConfigApplicationContext doLoad(Class[] configs, + private AnnotationConfigWebApplicationContext doLoad(Class[] configs, String... environment) { - AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext(); EnvironmentTestUtils.addEnvironment(applicationContext, environment); applicationContext.register(configs); - applicationContext.register(HttpEncodingAutoConfiguration.class); + applicationContext.register(MinimalWebAutoConfiguration.class, + HttpEncodingAutoConfiguration.class); + applicationContext.setServletContext(new MockServletContext()); applicationContext.refresh(); return applicationContext; } @@ -190,4 +224,19 @@ public class HttpEncodingAutoConfigurationTests { } + @Configuration + static class MinimalWebAutoConfiguration { + + @Bean + public MockEmbeddedServletContainerFactory mockEmbeddedServletContainerFactory() { + return new MockEmbeddedServletContainerFactory(); + } + + @Bean + public EmbeddedServletContainerCustomizerBeanPostProcessor + embeddedServletContainerCustomizerBeanPostProcessor() { + return new EmbeddedServletContainerCustomizerBeanPostProcessor(); + } + } + } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractConfigurableEmbeddedServletContainer.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractConfigurableEmbeddedServletContainer.java index c22785bd617..9b0e8b01758 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractConfigurableEmbeddedServletContainer.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractConfigurableEmbeddedServletContainer.java @@ -18,10 +18,14 @@ package org.springframework.boot.context.embedded; import java.io.File; import java.net.InetAddress; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -39,6 +43,7 @@ import org.springframework.util.ClassUtils; * @author Stephane Nicoll * @author Ivan Sopov * @author Eddú Meléndez + * @author Brian Clozel * @see AbstractEmbeddedServletContainerFactory */ public abstract class AbstractConfigurableEmbeddedServletContainer @@ -81,6 +86,8 @@ public abstract class AbstractConfigurableEmbeddedServletContainer private String serverHeader; + private Map localeCharsetMappings = new HashMap(); + /** * Create a new {@link AbstractConfigurableEmbeddedServletContainer} instance. */ @@ -327,6 +334,19 @@ public abstract class AbstractConfigurableEmbeddedServletContainer this.serverHeader = serverHeader; } + /** + * Return the Locale to Charset mappings. + * @return the charset mappings + */ + public Map getLocaleCharsetMappings() { + return this.localeCharsetMappings; + } + + public void setLocaleCharsetMappings(Map localeCharsetMappings) { + Assert.notNull(localeCharsetMappings, "localeCharsetMappings must not be null"); + this.localeCharsetMappings = localeCharsetMappings; + } + /** * Utility method that can be used by subclasses wishing to combine the specified * {@link ServletContextInitializer} parameters with those defined in this instance. diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/ConfigurableEmbeddedServletContainer.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/ConfigurableEmbeddedServletContainer.java index cbc413761ac..c3d44e351a6 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/ConfigurableEmbeddedServletContainer.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/ConfigurableEmbeddedServletContainer.java @@ -18,7 +18,10 @@ package org.springframework.boot.context.embedded; import java.io.File; import java.net.InetAddress; +import java.nio.charset.Charset; import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -34,6 +37,7 @@ import org.springframework.boot.web.servlet.ServletContextInitializer; * @author Andy Wilkinson * @author Stephane Nicoll * @author Eddú Meléndez + * @author Brian Clozel * @see EmbeddedServletContainerFactory * @see EmbeddedServletContainerCustomizer */ @@ -174,4 +178,10 @@ public interface ConfigurableEmbeddedServletContainer extends ErrorPageRegistry */ void setServerHeader(String serverHeader); + /** + * Sets the Locale to Charset mappings. + * @param localeCharsetMappings the Locale to Charset mappings + */ + void setLocaleCharsetMappings(Map localeCharsetMappings); + } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java index a80e3e22d6e..a2600c17092 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Set; import javax.servlet.ServletException; @@ -354,6 +355,10 @@ public class JettyEmbeddedServletContainerFactory addJspServlet(context); context.addBean(new JasperInitializer(context), true); } + for (Locale locale : getLocaleCharsetMappings().keySet()) { + context.addLocaleEncoding(locale.toString(), + getLocaleCharsetMappings().get(locale).toString()); + } ServletContextInitializer[] initializersToUse = mergeInitializers(initializers); Configuration[] configurations = getWebAppContextConfigurations(context, initializersToUse); diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java index 30e4c0b1921..4117307e259 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -193,6 +194,15 @@ public class TomcatEmbeddedServletContainerFactory context.setParentClassLoader( this.resourceLoader != null ? this.resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader()); + // override defaults, see org.apache.catalina.util.CharsetMapperDefault.properties + context.addLocaleEncodingMappingParameter(Locale.ENGLISH.toString(), + DEFAULT_CHARSET.displayName()); + context.addLocaleEncodingMappingParameter(Locale.FRENCH.toString(), + DEFAULT_CHARSET.displayName()); + for (Locale locale : getLocaleCharsetMappings().keySet()) { + context.addLocaleEncodingMappingParameter(locale.toString(), + getLocaleCharsetMappings().get(locale).toString()); + } try { context.setUseRelativeRedirects(false); } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java index 689bbaf880b..b4059ca9059 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Set; import javax.net.ssl.KeyManager; @@ -382,6 +383,10 @@ public class UndertowEmbeddedServletContainerFactory File dir = getValidSessionStoreDir(); deployment.setSessionPersistenceManager(new FileSessionPersistence(dir)); } + for (Locale locale : getLocaleCharsetMappings().keySet()) { + deployment.addLocaleCharsetMapping(locale.toString(), + getLocaleCharsetMappings().get(locale).toString()); + } DeploymentManager manager = Servlets.newContainer().addDeployment(deployment); manager.deploy(); SessionManager sessionManager = manager.getDeployment().getSessionManager(); diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java index c275d9663f0..d5371fd78bf 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java @@ -38,6 +38,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -870,6 +872,17 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { }); } + @Test + public void localeCharsetMappingsAreConfigured() throws Exception { + AbstractEmbeddedServletContainerFactory factory = getFactory(); + Map mappings = new HashMap(); + mappings.put(Locale.GERMAN, Charset.forName("UTF-8")); + factory.setLocaleCharsetMappings(mappings); + this.container = factory.getEmbeddedServletContainer(); + assertThat(getCharset(Locale.GERMAN).toString()).isEqualTo("UTF-8"); + assertThat(getCharset(Locale.ITALIAN)).isNull(); + } + protected abstract void addConnector(int port, AbstractEmbeddedServletContainerFactory factory); @@ -917,6 +930,8 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { return MimeMappings.DEFAULT.getAll(); } + protected abstract Charset getCharset(Locale locale); + private void addTestTxtFile(AbstractEmbeddedServletContainerFactory factory) throws IOException { FileCopyUtils.copy("test", diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java index f63ab4a3def..b5aefc400bb 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java @@ -17,8 +17,10 @@ package org.springframework.boot.context.embedded.jetty; import java.io.IOException; +import java.nio.charset.Charset; import java.util.Arrays; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -326,4 +328,11 @@ public class JettyEmbeddedServletContainerFactoryTests return context.getMimeTypes().getMimeMap(); } + @Override + protected Charset getCharset(Locale locale) { + WebAppContext context = (WebAppContext) ((JettyEmbeddedServletContainer) this.container) + .getServer().getHandler(); + String charsetName = context.getLocaleEncoding(locale); + return (charsetName != null) ? Charset.forName(charsetName) : null; + } } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java index 8695ef70993..9fa1ac466d1 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.Arrays; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -37,6 +38,7 @@ import org.apache.catalina.Valve; import org.apache.catalina.Wrapper; import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.util.CharsetMapper; import org.apache.catalina.valves.RemoteIpValve; import org.apache.tomcat.util.net.SSLHostConfig; import org.junit.After; @@ -430,6 +432,15 @@ public class TomcatEmbeddedServletContainerFactoryTests new InitialContext().lookup("java:comp/env"); } + @Test + public void defaultLocaleCharsetMappingsAreOverriden() throws Exception { + TomcatEmbeddedServletContainerFactory factory = getFactory(); + this.container = factory.getEmbeddedServletContainer(); + // override defaults, see org.apache.catalina.util.CharsetMapperDefault.properties + assertThat(getCharset(Locale.ENGLISH).toString()).isEqualTo("UTF-8"); + assertThat(getCharset(Locale.FRENCH).toString()).isEqualTo("UTF-8"); + } + @Override protected Wrapper getJspServlet() { Container context = ((TomcatEmbeddedServletContainer) this.container).getTomcat() @@ -446,6 +457,15 @@ public class TomcatEmbeddedServletContainerFactoryTests "mimeMappings"); } + @Override + protected Charset getCharset(Locale locale) { + Context context = (Context) ((TomcatEmbeddedServletContainer) this.container) + .getTomcat().getHost().findChildren()[0]; + CharsetMapper mapper = ((TomcatEmbeddedContext) context).getCharsetMapper(); + String charsetName = mapper.getCharset(locale); + return (charsetName != null) ? Charset.forName(charsetName) : null; + } + private void assertTimeout(TomcatEmbeddedServletContainerFactory factory, int expected) { Tomcat tomcat = getTomcat(factory); diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java index 3d4532e7120..1f7264c070b 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java @@ -19,9 +19,11 @@ package org.springframework.boot.context.embedded.undertow; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.charset.Charset; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; @@ -268,4 +270,11 @@ public class UndertowEmbeddedServletContainerFactoryTests return expectedMappings; } + @Override + protected Charset getCharset(Locale locale) { + DeploymentInfo info = ((DeploymentManager) ReflectionTestUtils + .getField(this.container, "manager")).getDeployment().getDeploymentInfo(); + String charsetName = info.getLocaleCharsetMapping().get(locale.toString()); + return (charsetName != null) ? Charset.forName(charsetName) : null; + } }