com.github.datalking.context.message.ResourceBundleMessageSource Maven / Gradle / Ivy
package com.github.datalking.context.message;
import com.github.datalking.util.Assert;
import com.github.datalking.util.ClassUtils;
import com.github.datalking.util.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
/**
* @author yaoo on 5/7/18
*/
public class ResourceBundleMessageSource extends AbstractMessageSource {
private String[] basenames = new String[0];
private String defaultEncoding;
private boolean fallbackToSystemLocale = true;
private long cacheMillis = -1;
private ClassLoader bundleClassLoader;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private final Map> cachedResourceBundles = new HashMap<>();
private final Map>> cachedBundleMsgFormats = new HashMap<>();
public void setBasename(String basename) {
setBasenames(basename);
}
public void setBasenames(String... basenames) {
if (basenames != null) {
this.basenames = new String[basenames.length];
for (int i = 0; i < basenames.length; i++) {
String basename = basenames[i];
Assert.hasText(basename, "Basename must not be empty");
this.basenames[i] = basename.trim();
}
} else {
this.basenames = new String[0];
}
}
public void setDefaultEncoding(String defaultEncoding) {
this.defaultEncoding = defaultEncoding;
}
public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
this.fallbackToSystemLocale = fallbackToSystemLocale;
}
public void setCacheSeconds(int cacheSeconds) {
this.cacheMillis = (cacheSeconds * 1000);
}
public void setBundleClassLoader(ClassLoader classLoader) {
this.bundleClassLoader = classLoader;
}
protected ClassLoader getBundleClassLoader() {
return (this.bundleClassLoader != null ? this.bundleClassLoader : this.beanClassLoader);
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
@Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
String result = null;
for (int i = 0; result == null && i < this.basenames.length; i++) {
ResourceBundle bundle = getResourceBundle(this.basenames[i], locale);
if (bundle != null) {
result = getStringOrNull(bundle, code);
}
}
return result;
}
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
MessageFormat messageFormat = null;
for (int i = 0; messageFormat == null && i < this.basenames.length; i++) {
ResourceBundle bundle = getResourceBundle(this.basenames[i], locale);
if (bundle != null) {
messageFormat = getMessageFormat(bundle, code, locale);
}
}
return messageFormat;
}
protected ResourceBundle getResourceBundle(String basename, Locale locale) {
if (this.cacheMillis >= 0) {
// Fresh ResourceBundle.getBundle call in order to let ResourceBundle
// do its native caching, at the expense of more extensive lookup steps.
return doGetBundle(basename, locale);
} else {
// Cache forever: prefer locale cache over repeated getBundle calls.
synchronized (this.cachedResourceBundles) {
Map localeMap = this.cachedResourceBundles.get(basename);
if (localeMap != null) {
ResourceBundle bundle = localeMap.get(locale);
if (bundle != null) {
return bundle;
}
}
try {
ResourceBundle bundle = doGetBundle(basename, locale);
if (localeMap == null) {
localeMap = new HashMap<>();
this.cachedResourceBundles.put(basename, localeMap);
}
localeMap.put(locale, bundle);
return bundle;
} catch (MissingResourceException ex) {
if (logger.isWarnEnabled()) {
logger.warn("ResourceBundle [" + basename + "] not found for MessageSource: " + ex.getMessage());
}
// Assume bundle not found -> do NOT throw the exception to allow for checking parent message source.
return null;
}
}
}
}
protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException {
if ((this.defaultEncoding != null && !"ISO-8859-1".equals(this.defaultEncoding)) ||
!this.fallbackToSystemLocale || this.cacheMillis >= 0) {
// if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
// throw new IllegalStateException("Cannot use 'defaultEncoding', 'fallbackToSystemLocale' and " +
// "'cacheSeconds' on the standard ResourceBundleMessageSource when running on Java 5. " +
// "Consider using ReloadableResourceBundleMessageSource instead.");
// }
return new ControlBasedResourceBundleFactory().getBundle(basename, locale);
} else {
// Good old standard call...
return ResourceBundle.getBundle(basename, locale, getBundleClassLoader());
}
}
protected MessageFormat getMessageFormat(ResourceBundle bundle, String code, Locale locale)
throws MissingResourceException {
synchronized (this.cachedBundleMsgFormats) {
Map> codeMap = this.cachedBundleMsgFormats.get(bundle);
Map localeMap = null;
if (codeMap != null) {
localeMap = codeMap.get(code);
if (localeMap != null) {
MessageFormat result = localeMap.get(locale);
if (result != null) {
return result;
}
}
}
String msg = getStringOrNull(bundle, code);
if (msg != null) {
if (codeMap == null) {
codeMap = new HashMap<>();
this.cachedBundleMsgFormats.put(bundle, codeMap);
}
if (localeMap == null) {
localeMap = new HashMap<>();
codeMap.put(code, localeMap);
}
MessageFormat result = createMessageFormat(msg, locale);
localeMap.put(locale, result);
return result;
}
return null;
}
}
private String getStringOrNull(ResourceBundle bundle, String key) {
try {
return bundle.getString(key);
} catch (MissingResourceException ex) {
// Assume key not found
// -> do NOT throw the exception to allow for checking parent message source.
return null;
}
}
@Override
public String toString() {
return getClass().getName() + ": basenames=[" + StringUtils.arrayToCommaDelimitedString(this.basenames) + "]";
}
private class ControlBasedResourceBundleFactory {
public ResourceBundle getBundle(String basename, Locale locale) {
return ResourceBundle.getBundle(basename, locale, getBundleClassLoader(), new MessageSourceControl());
}
}
private class MessageSourceControl extends ResourceBundle.Control {
@Override
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
// Special handling of default encoding
if (format.equals("java.properties")) {
String bundleName = toBundleName(baseName, locale);
final String resourceName = toResourceName(bundleName, "properties");
final ClassLoader classLoader = loader;
final boolean reloadFlag = reload;
InputStream stream;
try {
stream = AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public InputStream run() throws IOException {
InputStream is = null;
if (reloadFlag) {
URL url = classLoader.getResource(resourceName);
if (url != null) {
URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
is = connection.getInputStream();
}
}
} else {
is = classLoader.getResourceAsStream(resourceName);
}
return is;
}
});
} catch (PrivilegedActionException ex) {
throw (IOException) ex.getException();
}
if (stream != null) {
try {
return (defaultEncoding != null ?
new PropertyResourceBundle(new InputStreamReader(stream, defaultEncoding)) :
new PropertyResourceBundle(stream));
} finally {
stream.close();
}
} else {
return null;
}
} else {
// Delegate handling of "java.class" format to standard Control
return super.newBundle(baseName, locale, format, loader, reload);
}
}
@Override
public Locale getFallbackLocale(String baseName, Locale locale) {
return (fallbackToSystemLocale ? super.getFallbackLocale(baseName, locale) : null);
}
@Override
public long getTimeToLive(String baseName, Locale locale) {
return (cacheMillis >= 0 ? cacheMillis : super.getTimeToLive(baseName, locale));
}
@Override
public boolean needsReload(String baseName, Locale locale, String format, ClassLoader loader, ResourceBundle bundle, long loadTime) {
if (super.needsReload(baseName, locale, format, loader, bundle, loadTime)) {
cachedBundleMsgFormats.remove(bundle);
return true;
} else {
return false;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy