mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Introduce setDefaultCharset() in AbstractResourceBasedMessageSource
Traditionally, AbstractResourceBasedMessageSource has only had a setDefaultEncoding() method which accepts the name of the default encoding character set. However, although we have recently made a concerted effort within the framework to introduce support for supplying a Charset instead of a character set's name, we had overlooked this particular scenario. In light of that, this commit introduces setDefaultCharset(Charset) and getDefaultCharset() methods in AbstractResourceBasedMessageSource and makes direct use of the available Charset in ReloadableResourceBundleMessageSource and ResourceBundleMessageSource. Furthermore, although technically a regression in behavior, invoking setDefaultEncoding() on such MessageSource implementations with an invalid character set name now results in an immediate UnsupportedCharsetException at configuration time instead of a NoSuchMessageException at runtime, which will help users to more easily detect misconfiguration. Closes gh-36413
This commit is contained in:
+33
-4
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.context.support;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
@@ -32,6 +33,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* configuration methods and corresponding semantic definitions.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 4.3
|
||||
* @see ResourceBundleMessageSource
|
||||
* @see ReloadableResourceBundleMessageSource
|
||||
@@ -40,7 +42,7 @@ public abstract class AbstractResourceBasedMessageSource extends AbstractMessage
|
||||
|
||||
private final Set<String> basenameSet = new LinkedHashSet<>(4);
|
||||
|
||||
private @Nullable String defaultEncoding;
|
||||
private @Nullable Charset defaultCharset;
|
||||
|
||||
private boolean fallbackToSystemLocale = true;
|
||||
|
||||
@@ -118,25 +120,52 @@ public abstract class AbstractResourceBasedMessageSource extends AbstractMessage
|
||||
|
||||
/**
|
||||
* Set the default charset to use for parsing properties files.
|
||||
* Used if no file-specific charset is specified for a file.
|
||||
* <p>Used if no file-specific charset is specified for a file.
|
||||
* <p>The effective default is the {@code java.util.Properties}
|
||||
* default encoding: ISO-8859-1. A {@code null} value indicates
|
||||
* the platform default encoding.
|
||||
* <p>Only applies to classic properties files, not to XML files.
|
||||
* @param defaultEncoding the default charset
|
||||
* @see #setDefaultCharset(Charset)
|
||||
*/
|
||||
public void setDefaultEncoding(@Nullable String defaultEncoding) {
|
||||
this.defaultEncoding = defaultEncoding;
|
||||
this.defaultCharset = (defaultEncoding != null ? Charset.forName(defaultEncoding) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default charset to use for parsing properties files, if any.
|
||||
* @since 4.3
|
||||
* @see #getDefaultCharset()
|
||||
*/
|
||||
protected @Nullable String getDefaultEncoding() {
|
||||
return this.defaultEncoding;
|
||||
return (this.defaultCharset != null ? this.defaultCharset.name() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default {@link Charset} to use for parsing properties files.
|
||||
* <p>Used if no file-specific charset is specified for a file.
|
||||
* <p>The effective default is the {@code java.util.Properties}
|
||||
* default encoding: ISO-8859-1. A {@code null} value indicates
|
||||
* the platform default encoding.
|
||||
* <p>Only applies to classic properties files, not to XML files.
|
||||
* @param defaultCharset the default charset
|
||||
* @since 7.0.6
|
||||
* @see #setDefaultEncoding(String)
|
||||
*/
|
||||
public void setDefaultCharset(@Nullable Charset defaultCharset) {
|
||||
this.defaultCharset = defaultCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default charset to use for parsing properties files, if any.
|
||||
* @since 7.0.6
|
||||
* @see #setDefaultCharset(Charset)
|
||||
*/
|
||||
protected @Nullable Charset getDefaultCharset() {
|
||||
return this.defaultCharset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to fall back to the system Locale if no files for a specific
|
||||
* Locale have been found. Default is "true"; if this is turned off, the only
|
||||
|
||||
+17
-12
@@ -19,6 +19,7 @@ package org.springframework.context.support;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -80,9 +81,10 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sebastien Deleuze
|
||||
* @author Sam Brannen
|
||||
* @see #setCacheSeconds
|
||||
* @see #setBasenames
|
||||
* @see #setDefaultEncoding
|
||||
* @see #setDefaultCharset
|
||||
* @see #setFileEncodings
|
||||
* @see #setPropertiesPersister
|
||||
* @see #setResourceLoader
|
||||
@@ -135,7 +137,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
|
||||
/**
|
||||
* Set per-file charsets to use for parsing properties files.
|
||||
* <p>Only applies to classic properties files, not to XML files.
|
||||
* @param fileEncodings a Properties with filenames as keys and charset
|
||||
* @param fileEncodings a Properties object with filenames as keys and charset
|
||||
* names as values. Filenames have to match the basename syntax,
|
||||
* with optional locale-specific components: for example, "WEB-INF/messages"
|
||||
* or "WEB-INF/messages_en".
|
||||
@@ -567,18 +569,21 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
|
||||
this.propertiesPersister.loadFromXml(props, is);
|
||||
}
|
||||
else {
|
||||
String encoding = null;
|
||||
Charset charset = null;
|
||||
if (this.fileEncodings != null) {
|
||||
encoding = this.fileEncodings.getProperty(filename);
|
||||
}
|
||||
if (encoding == null) {
|
||||
encoding = getDefaultEncoding();
|
||||
}
|
||||
if (encoding != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loading properties [" + resource.getFilename() + "] with encoding '" + encoding + "'");
|
||||
String charsetName = this.fileEncodings.getProperty(filename);
|
||||
if (charsetName != null) {
|
||||
charset = Charset.forName(charsetName);
|
||||
}
|
||||
this.propertiesPersister.load(props, new InputStreamReader(is, encoding));
|
||||
}
|
||||
if (charset == null) {
|
||||
charset = getDefaultCharset();
|
||||
}
|
||||
if (charset != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loading properties [" + resource.getFilename() + "] with encoding '" + charset + "'");
|
||||
}
|
||||
this.propertiesPersister.load(props, new InputStreamReader(is, charset));
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
||||
+18
-15
@@ -22,6 +22,8 @@ import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -57,11 +59,11 @@ import org.springframework.util.ClassUtils;
|
||||
* This means that "test.theme" is effectively equivalent to "test/theme".
|
||||
*
|
||||
* <p>On the classpath, bundle resources will be read with the locally configured
|
||||
* {@link #setDefaultEncoding encoding}: by default, ISO-8859-1; consider switching
|
||||
* {@link #setDefaultCharset Charset}: by default, ISO-8859-1; consider switching
|
||||
* this to UTF-8, or to {@code null} for the platform default encoding. On the JDK 9+
|
||||
* module path where locally provided {@code ResourceBundle.Control} handles are not
|
||||
* supported, this MessageSource always falls back to {@link ResourceBundle#getBundle}
|
||||
* retrieval with the platform default encoding: UTF-8 with a ISO-8859-1 fallback on
|
||||
* retrieval with the platform default encoding: UTF-8 with an ISO-8859-1 fallback on
|
||||
* JDK 9+ (configurable through the "java.util.PropertyResourceBundle.encoding" system
|
||||
* property). Note that {@link #loadBundle(Reader)}/{@link #loadBundle(InputStream)}
|
||||
* won't be called in this case either, effectively ignoring overrides in subclasses.
|
||||
@@ -70,6 +72,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Qimiao Chen
|
||||
* @author Sam Brannen
|
||||
* @see #setBasenames
|
||||
* @see ReloadableResourceBundleMessageSource
|
||||
* @see java.util.ResourceBundle
|
||||
@@ -83,7 +86,7 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
|
||||
|
||||
/**
|
||||
* Cache to hold loaded ResourceBundles.
|
||||
* This Map is keyed with the bundle basename, which holds a Map that is
|
||||
* <p>This Map is keyed with the bundle basename, which holds a Map that is
|
||||
* keyed with the Locale and in turn holds the ResourceBundle instances.
|
||||
* This allows for very efficient hash lookups, significantly faster
|
||||
* than the ResourceBundle class's own cache.
|
||||
@@ -93,7 +96,7 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
|
||||
|
||||
/**
|
||||
* Cache to hold already generated MessageFormats.
|
||||
* This Map is keyed with the ResourceBundle, which holds a Map that is
|
||||
* <p>This Map is keyed with the ResourceBundle, which holds a Map that is
|
||||
* keyed with the message code, which in turn holds a Map that is keyed
|
||||
* with the Locale and holds the MessageFormat values. This allows for
|
||||
* very efficient hash lookups without concatenated keys.
|
||||
@@ -106,7 +109,7 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
|
||||
|
||||
|
||||
public ResourceBundleMessageSource() {
|
||||
setDefaultEncoding("ISO-8859-1");
|
||||
setDefaultCharset(StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
|
||||
@@ -239,12 +242,12 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
|
||||
catch (UnsupportedOperationException ex) {
|
||||
// Probably in a Java Module System environment on JDK 9+
|
||||
this.control = null;
|
||||
String encoding = getDefaultEncoding();
|
||||
if (encoding != null && logger.isInfoEnabled()) {
|
||||
Charset charset = getDefaultCharset();
|
||||
if (charset != null && logger.isInfoEnabled()) {
|
||||
logger.info("ResourceBundleMessageSource is configured to read resources with encoding '" +
|
||||
encoding + "' but ResourceBundle.Control is not supported in current system environment: " +
|
||||
charset + "' but ResourceBundle.Control is not supported in current system environment: " +
|
||||
ex.getMessage() + " - falling back to plain ResourceBundle.getBundle retrieval with the " +
|
||||
"platform default encoding. Consider setting the 'defaultEncoding' property to 'null' " +
|
||||
"platform default encoding. Consider setting the 'defaultCharset' property to 'null' " +
|
||||
"for participating in the platform default and therefore avoiding this log message.");
|
||||
}
|
||||
}
|
||||
@@ -256,7 +259,7 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
|
||||
|
||||
/**
|
||||
* Load a property-based resource bundle from the given reader.
|
||||
* <p>This will be called in case of a {@link #setDefaultEncoding "defaultEncoding"},
|
||||
* <p>This will be called in case of a {@linkplain #setDefaultCharset "defaultCharset"},
|
||||
* including {@link ResourceBundleMessageSource}'s default ISO-8859-1 encoding.
|
||||
* Note that this method can only be called with a {@code ResourceBundle.Control}:
|
||||
* When running on the JDK 9+ module path where such control handles are not
|
||||
@@ -276,9 +279,9 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
|
||||
/**
|
||||
* Load a property-based resource bundle from the given input stream,
|
||||
* picking up the default properties encoding on JDK 9+.
|
||||
* <p>This will only be called with {@link #setDefaultEncoding "defaultEncoding"}
|
||||
* <p>This will only be called with {@linkplain #setDefaultCharset "defaultCharset"}
|
||||
* set to {@code null}, explicitly enforcing the platform default encoding
|
||||
* (which is UTF-8 with a ISO-8859-1 fallback on JDK 9+ but configurable
|
||||
* (which is UTF-8 with an ISO-8859-1 fallback on JDK 9+ but configurable
|
||||
* through the "java.util.PropertyResourceBundle.encoding" system property).
|
||||
* Note that this method can only be called with a {@code ResourceBundle.Control}:
|
||||
* When running on the JDK 9+ module path where such control handles are not
|
||||
@@ -404,9 +407,9 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
|
||||
inputStream = classLoader.getResourceAsStream(resourceName);
|
||||
}
|
||||
if (inputStream != null) {
|
||||
String encoding = getDefaultEncoding();
|
||||
if (encoding != null) {
|
||||
try (InputStreamReader bundleReader = new InputStreamReader(inputStream, encoding)) {
|
||||
Charset charset = getDefaultCharset();
|
||||
if (charset != null) {
|
||||
try (InputStreamReader bundleReader = new InputStreamReader(inputStream, charset)) {
|
||||
return loadBundle(bundleReader);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user