提交 a000dd78 编写于 作者: J Juergen Hoeller

ReloadableResourceBundleMessageSource uses ConcurrentHashMaps and...

ReloadableResourceBundleMessageSource uses ConcurrentHashMaps and ReentrantLocks instead of synchronization

Issue: SPR-10500
上级 f5cf3cd5
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -21,11 +21,13 @@ import java.io.InputStream; ...@@ -21,11 +21,13 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.ResourceLoaderAware; import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.DefaultResourceLoader;
...@@ -92,8 +94,7 @@ import org.springframework.util.StringUtils; ...@@ -92,8 +94,7 @@ import org.springframework.util.StringUtils;
* @see ResourceBundleMessageSource * @see ResourceBundleMessageSource
* @see java.util.ResourceBundle * @see java.util.ResourceBundle
*/ */
public class ReloadableResourceBundleMessageSource extends AbstractMessageSource public class ReloadableResourceBundleMessageSource extends AbstractMessageSource implements ResourceLoaderAware {
implements ResourceLoaderAware {
private static final String PROPERTIES_SUFFIX = ".properties"; private static final String PROPERTIES_SUFFIX = ".properties";
...@@ -110,19 +111,23 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource ...@@ -110,19 +111,23 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
private long cacheMillis = -1; private long cacheMillis = -1;
private boolean concurrentRefresh = true;
private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister(); private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
private ResourceLoader resourceLoader = new DefaultResourceLoader(); private ResourceLoader resourceLoader = new DefaultResourceLoader();
/** Cache to hold filename lists per Locale */ /** Cache to hold filename lists per Locale */
private final Map<String, Map<Locale, List<String>>> cachedFilenames = private final ConcurrentMap<String, Map<Locale, List<String>>> cachedFilenames =
new HashMap<String, Map<Locale, List<String>>>(); new ConcurrentHashMap<String, Map<Locale, List<String>>>();
/** Cache to hold already loaded properties per filename */ /** Cache to hold already loaded properties per filename */
private final Map<String, PropertiesHolder> cachedProperties = new HashMap<String, PropertiesHolder>(); private final ConcurrentMap<String, PropertiesHolder> cachedProperties =
new ConcurrentHashMap<String, PropertiesHolder>();
/** Cache to hold merged loaded properties per locale */ /** Cache to hold merged loaded properties per locale */
private final Map<Locale, PropertiesHolder> cachedMergedProperties = new HashMap<Locale, PropertiesHolder>(); private final ConcurrentMap<Locale, PropertiesHolder> cachedMergedProperties =
new ConcurrentHashMap<Locale, PropertiesHolder>();
/** /**
...@@ -231,6 +236,20 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource ...@@ -231,6 +236,20 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
this.cacheMillis = (cacheSeconds * 1000); this.cacheMillis = (cacheSeconds * 1000);
} }
/**
* Specify whether to allow for concurrent refresh behavior, i.e. one thread
* locked in a refresh attempt for a specific cached properties file whereas
* other threads keep returning the old properties for the time being, until
* the refresh attempt has completed.
* <p>Default is "true": This behavior is new as of Spring Framework 4.1,
* minimizing contention between threads. If you prefer the old behavior,
* i.e. to fully block on refresh, switch this flag to "false".
* @see #setCacheSeconds
*/
public void setConcurrentRefresh(boolean concurrentRefresh) {
this.concurrentRefresh = concurrentRefresh;
}
/** /**
* Set the PropertiesPersister to use for parsing properties files. * Set the PropertiesPersister to use for parsing properties files.
* <p>The default is a DefaultPropertiesPersister. * <p>The default is a DefaultPropertiesPersister.
...@@ -322,26 +341,27 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource ...@@ -322,26 +341,27 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
* cached forever. * cached forever.
*/ */
protected PropertiesHolder getMergedProperties(Locale locale) { protected PropertiesHolder getMergedProperties(Locale locale) {
synchronized (this.cachedMergedProperties) { PropertiesHolder mergedHolder = this.cachedMergedProperties.get(locale);
PropertiesHolder mergedHolder = this.cachedMergedProperties.get(locale); if (mergedHolder != null) {
if (mergedHolder != null) { return mergedHolder;
return mergedHolder; }
} Properties mergedProps = new Properties();
Properties mergedProps = new Properties(); mergedHolder = new PropertiesHolder(mergedProps, -1);
mergedHolder = new PropertiesHolder(mergedProps, -1); for (int i = this.basenames.length - 1; i >= 0; i--) {
for (int i = this.basenames.length - 1; i >= 0; i--) { List<String> filenames = calculateAllFilenames(this.basenames[i], locale);
List<String> filenames = calculateAllFilenames(this.basenames[i], locale); for (int j = filenames.size() - 1; j >= 0; j--) {
for (int j = filenames.size() - 1; j >= 0; j--) { String filename = filenames.get(j);
String filename = filenames.get(j); PropertiesHolder propHolder = getProperties(filename);
PropertiesHolder propHolder = getProperties(filename); if (propHolder.getProperties() != null) {
if (propHolder.getProperties() != null) { mergedProps.putAll(propHolder.getProperties());
mergedProps.putAll(propHolder.getProperties());
}
} }
} }
this.cachedMergedProperties.put(locale, mergedHolder);
return mergedHolder;
} }
PropertiesHolder existing = this.cachedMergedProperties.putIfAbsent(locale, mergedHolder);
if (existing != null) {
mergedHolder = existing;
}
return mergedHolder;
} }
/** /**
...@@ -355,36 +375,34 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource ...@@ -355,36 +375,34 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
* @see #calculateFilenamesForLocale * @see #calculateFilenamesForLocale
*/ */
protected List<String> calculateAllFilenames(String basename, Locale locale) { protected List<String> calculateAllFilenames(String basename, Locale locale) {
synchronized (this.cachedFilenames) { Map<Locale, List<String>> localeMap = this.cachedFilenames.get(basename);
Map<Locale, List<String>> localeMap = this.cachedFilenames.get(basename); if (localeMap != null) {
if (localeMap != null) { List<String> filenames = localeMap.get(locale);
List<String> filenames = localeMap.get(locale); if (filenames != null) {
if (filenames != null) { return filenames;
return filenames;
}
} }
List<String> filenames = new ArrayList<String>(7); }
filenames.addAll(calculateFilenamesForLocale(basename, locale)); List<String> filenames = new ArrayList<String>(7);
if (this.fallbackToSystemLocale && !locale.equals(Locale.getDefault())) { filenames.addAll(calculateFilenamesForLocale(basename, locale));
List<String> fallbackFilenames = calculateFilenamesForLocale(basename, Locale.getDefault()); if (this.fallbackToSystemLocale && !locale.equals(Locale.getDefault())) {
for (String fallbackFilename : fallbackFilenames) { List<String> fallbackFilenames = calculateFilenamesForLocale(basename, Locale.getDefault());
if (!filenames.contains(fallbackFilename)) { for (String fallbackFilename : fallbackFilenames) {
// Entry for fallback locale that isn't already in filenames list. if (!filenames.contains(fallbackFilename)) {
filenames.add(fallbackFilename); // Entry for fallback locale that isn't already in filenames list.
} filenames.add(fallbackFilename);
} }
} }
filenames.add(basename); }
if (localeMap != null) { filenames.add(basename);
localeMap.put(locale, filenames); if (localeMap == null) {
} localeMap = new ConcurrentHashMap<Locale, List<String>>();
else { Map<Locale, List<String>> existing = this.cachedFilenames.putIfAbsent(basename, localeMap);
localeMap = new HashMap<Locale, List<String>>(); if (existing != null) {
localeMap.put(locale, filenames); localeMap = existing;
this.cachedFilenames.put(basename, localeMap);
} }
return filenames;
} }
localeMap.put(locale, filenames);
return filenames;
} }
/** /**
...@@ -432,16 +450,46 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource ...@@ -432,16 +450,46 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
* @return the current PropertiesHolder for the bundle * @return the current PropertiesHolder for the bundle
*/ */
protected PropertiesHolder getProperties(String filename) { protected PropertiesHolder getProperties(String filename) {
synchronized (this.cachedProperties) { PropertiesHolder propHolder = this.cachedProperties.get(filename);
PropertiesHolder propHolder = this.cachedProperties.get(filename); long originalTimestamp = -1;
if (propHolder != null &&
(propHolder.getRefreshTimestamp() < 0 || if (propHolder != null) {
propHolder.getRefreshTimestamp() > System.currentTimeMillis() - this.cacheMillis)) { originalTimestamp = propHolder.getRefreshTimestamp();
// up to date if (originalTimestamp < 0 || originalTimestamp > System.currentTimeMillis() - this.cacheMillis) {
// Up to date
return propHolder;
}
}
else {
propHolder = new PropertiesHolder();
PropertiesHolder existingHolder = this.cachedProperties.putIfAbsent(filename, propHolder);
if (existingHolder != null) {
propHolder = existingHolder;
}
}
// At this point, we need to refresh...
if (this.concurrentRefresh && propHolder.getRefreshTimestamp() >= 0) {
// A populated but stale holder -> could keep using it.
if (!propHolder.refreshLock.tryLock()) {
// Getting refreshed by another thread already ->
// let's return the existing properties for the time being.
return propHolder; return propHolder;
} }
}
else {
propHolder.refreshLock.lock();
}
try {
PropertiesHolder existingHolder = this.cachedProperties.get(filename);
if (existingHolder != null && existingHolder.getRefreshTimestamp() > originalTimestamp) {
return existingHolder;
}
return refreshProperties(filename, propHolder); return refreshProperties(filename, propHolder);
} }
finally {
propHolder.refreshLock.unlock();
}
} }
/** /**
...@@ -476,8 +524,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource ...@@ -476,8 +524,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
catch (IOException ex) { catch (IOException ex) {
// Probably a class path resource: cache it forever. // Probably a class path resource: cache it forever.
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug( logger.debug(resource + " could not be resolved in the file system - assuming that it hasn't changed", ex);
resource + " could not be resolved in the file system - assuming that is hasn't changed", ex);
} }
fileTimestamp = -1; fileTimestamp = -1;
} }
...@@ -561,12 +608,8 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource ...@@ -561,12 +608,8 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
*/ */
public void clearCache() { public void clearCache() {
logger.debug("Clearing entire resource bundle cache"); logger.debug("Clearing entire resource bundle cache");
synchronized (this.cachedProperties) { this.cachedProperties.clear();
this.cachedProperties.clear(); this.cachedMergedProperties.clear();
}
synchronized (this.cachedMergedProperties) {
this.cachedMergedProperties.clear();
}
} }
/** /**
...@@ -595,30 +638,34 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource ...@@ -595,30 +638,34 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
*/ */
protected class PropertiesHolder { protected class PropertiesHolder {
private Properties properties; private final Properties properties;
private long fileTimestamp = -1; private final long fileTimestamp;
private long refreshTimestamp = -1; private volatile long refreshTimestamp = -2;
private final ReentrantLock refreshLock = new ReentrantLock();
/** Cache to hold already generated MessageFormats per message code */ /** Cache to hold already generated MessageFormats per message code */
private final Map<String, Map<Locale, MessageFormat>> cachedMessageFormats = private final ConcurrentMap<String, Map<Locale, MessageFormat>> cachedMessageFormats =
new HashMap<String, Map<Locale, MessageFormat>>(); new ConcurrentHashMap<String, Map<Locale, MessageFormat>>();
public PropertiesHolder() {
this.properties = null;
this.fileTimestamp = -1;
}
public PropertiesHolder(Properties properties, long fileTimestamp) { public PropertiesHolder(Properties properties, long fileTimestamp) {
this.properties = properties; this.properties = properties;
this.fileTimestamp = fileTimestamp; this.fileTimestamp = fileTimestamp;
} }
public PropertiesHolder() {
}
public Properties getProperties() { public Properties getProperties() {
return properties; return this.properties;
} }
public long getFileTimestamp() { public long getFileTimestamp() {
return fileTimestamp; return this.fileTimestamp;
} }
public void setRefreshTimestamp(long refreshTimestamp) { public void setRefreshTimestamp(long refreshTimestamp) {
...@@ -626,7 +673,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource ...@@ -626,7 +673,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
} }
public long getRefreshTimestamp() { public long getRefreshTimestamp() {
return refreshTimestamp; return this.refreshTimestamp;
} }
public String getProperty(String code) { public String getProperty(String code) {
...@@ -640,26 +687,27 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource ...@@ -640,26 +687,27 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
if (this.properties == null) { if (this.properties == null) {
return null; return null;
} }
synchronized (this.cachedMessageFormats) { Map<Locale, MessageFormat> localeMap = this.cachedMessageFormats.get(code);
Map<Locale, MessageFormat> localeMap = this.cachedMessageFormats.get(code); if (localeMap != null) {
if (localeMap != null) { MessageFormat result = localeMap.get(locale);
MessageFormat result = localeMap.get(locale); if (result != null) {
if (result != null) { return result;
return result;
}
} }
String msg = this.properties.getProperty(code); }
if (msg != null) { String msg = this.properties.getProperty(code);
if (localeMap == null) { if (msg != null) {
localeMap = new HashMap<Locale, MessageFormat>(); if (localeMap == null) {
this.cachedMessageFormats.put(code, localeMap); localeMap = new ConcurrentHashMap<Locale, MessageFormat>();
Map<Locale, MessageFormat> existing = this.cachedMessageFormats.putIfAbsent(code, localeMap);
if (existing != null) {
localeMap = existing;
} }
MessageFormat result = createMessageFormat(msg, locale);
localeMap.put(locale, result);
return result;
} }
return null; MessageFormat result = createMessageFormat(msg, locale);
localeMap.put(locale, result);
return result;
} }
return null;
} }
} }
......
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -21,62 +21,68 @@ import java.util.Locale; ...@@ -21,62 +21,68 @@ import java.util.Locale;
import java.util.Properties; import java.util.Properties;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import junit.framework.TestCase; import org.junit.After;
import org.junit.Test;
import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.MutablePropertyValues;
import org.springframework.context.MessageSourceResolvable; import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException; import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.JdkVersion;
import static org.junit.Assert.*;
/** /**
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 03.02.2004 * @since 03.02.2004
*/ */
public class ResourceBundleMessageSourceTests extends TestCase { public class ResourceBundleMessageSourceTests {
@Test
public void testMessageAccessWithDefaultMessageSource() { public void testMessageAccessWithDefaultMessageSource() {
doTestMessageAccess(false, true, false, false, false); doTestMessageAccess(false, true, false, false, false);
} }
@Test
public void testMessageAccessWithDefaultMessageSourceAndMessageFormat() { public void testMessageAccessWithDefaultMessageSourceAndMessageFormat() {
doTestMessageAccess(false, true, false, false, true); doTestMessageAccess(false, true, false, false, true);
} }
@Test
public void testMessageAccessWithDefaultMessageSourceAndFallbackToGerman() { public void testMessageAccessWithDefaultMessageSourceAndFallbackToGerman() {
doTestMessageAccess(false, true, true, true, false); doTestMessageAccess(false, true, true, true, false);
} }
@Test
public void testMessageAccessWithDefaultMessageSourceAndFallbackTurnedOff() { public void testMessageAccessWithDefaultMessageSourceAndFallbackTurnedOff() {
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
return;
}
doTestMessageAccess(false, false, false, false, false); doTestMessageAccess(false, false, false, false, false);
} }
@Test
public void testMessageAccessWithDefaultMessageSourceAndFallbackTurnedOffAndFallbackToGerman() { public void testMessageAccessWithDefaultMessageSourceAndFallbackTurnedOffAndFallbackToGerman() {
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
return;
}
doTestMessageAccess(false, false, true, true, false); doTestMessageAccess(false, false, true, true, false);
} }
@Test
public void testMessageAccessWithReloadableMessageSource() { public void testMessageAccessWithReloadableMessageSource() {
doTestMessageAccess(true, true, false, false, false); doTestMessageAccess(true, true, false, false, false);
} }
@Test
public void testMessageAccessWithReloadableMessageSourceAndMessageFormat() { public void testMessageAccessWithReloadableMessageSourceAndMessageFormat() {
doTestMessageAccess(true, true, false, false, true); doTestMessageAccess(true, true, false, false, true);
} }
@Test
public void testMessageAccessWithReloadableMessageSourceAndFallbackToGerman() { public void testMessageAccessWithReloadableMessageSourceAndFallbackToGerman() {
doTestMessageAccess(true, true, true, true, false); doTestMessageAccess(true, true, true, true, false);
} }
@Test
public void testMessageAccessWithReloadableMessageSourceAndFallbackTurnedOff() { public void testMessageAccessWithReloadableMessageSourceAndFallbackTurnedOff() {
doTestMessageAccess(true, false, false, false, false); doTestMessageAccess(true, false, false, false, false);
} }
@Test
public void testMessageAccessWithReloadableMessageSourceAndFallbackTurnedOffAndFallbackToGerman() { public void testMessageAccessWithReloadableMessageSourceAndFallbackTurnedOffAndFallbackToGerman() {
doTestMessageAccess(true, false, true, true, false); doTestMessageAccess(true, false, true, true, false);
} }
...@@ -94,7 +100,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -94,7 +100,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
MutablePropertyValues pvs = new MutablePropertyValues(); MutablePropertyValues pvs = new MutablePropertyValues();
String basepath = "org/springframework/context/support/"; String basepath = "org/springframework/context/support/";
String[] basenames = null; String[] basenames;
if (reloadable) { if (reloadable) {
basenames = new String[] { basenames = new String[] {
"classpath:" + basepath + "messages", "classpath:" + basepath + "messages",
...@@ -129,7 +135,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -129,7 +135,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("nochricht2", ac.getMessage("code2", null, new Locale("DE", "at"))); assertEquals("nochricht2", ac.getMessage("code2", null, new Locale("DE", "at")));
assertEquals("noochricht2", ac.getMessage("code2", null, new Locale("DE", "at", "oo"))); assertEquals("noochricht2", ac.getMessage("code2", null, new Locale("DE", "at", "oo")));
if (reloadable && JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_15) { if (reloadable) {
assertEquals("nachricht2xml", ac.getMessage("code2", null, Locale.GERMANY)); assertEquals("nachricht2xml", ac.getMessage("code2", null, Locale.GERMANY));
} }
...@@ -196,6 +202,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -196,6 +202,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
} }
} }
@Test
public void testDefaultApplicationContextMessageSource() { public void testDefaultApplicationContextMessageSource() {
GenericApplicationContext ac = new GenericApplicationContext(); GenericApplicationContext ac = new GenericApplicationContext();
ac.refresh(); ac.refresh();
...@@ -203,6 +210,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -203,6 +210,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("default value", ac.getMessage("code1", new Object[] {"value"}, "default {0}", Locale.ENGLISH)); assertEquals("default value", ac.getMessage("code1", new Object[] {"value"}, "default {0}", Locale.ENGLISH));
} }
@Test
public void testResourceBundleMessageSourceStandalone() { public void testResourceBundleMessageSourceStandalone() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource(); ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages"); ms.setBasename("org/springframework/context/support/messages");
...@@ -210,6 +218,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -210,6 +218,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN)); assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
} }
@Test
public void testResourceBundleMessageSourceWithWhitespaceInBasename() { public void testResourceBundleMessageSourceWithWhitespaceInBasename() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource(); ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasename(" org/springframework/context/support/messages "); ms.setBasename(" org/springframework/context/support/messages ");
...@@ -217,6 +226,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -217,6 +226,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN)); assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
} }
@Test
public void testResourceBundleMessageSourceWithDefaultCharset() { public void testResourceBundleMessageSourceWithDefaultCharset() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource(); ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages"); ms.setBasename("org/springframework/context/support/messages");
...@@ -225,10 +235,8 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -225,10 +235,8 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN)); assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
} }
@Test
public void testResourceBundleMessageSourceWithInappropriateDefaultCharset() { public void testResourceBundleMessageSourceWithInappropriateDefaultCharset() {
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
return;
}
ResourceBundleMessageSource ms = new ResourceBundleMessageSource(); ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages"); ms.setBasename("org/springframework/context/support/messages");
ms.setDefaultEncoding("argh"); ms.setDefaultEncoding("argh");
...@@ -242,6 +250,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -242,6 +250,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
} }
} }
@Test
public void testReloadableResourceBundleMessageSourceStandalone() { public void testReloadableResourceBundleMessageSourceStandalone() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages"); ms.setBasename("org/springframework/context/support/messages");
...@@ -249,6 +258,36 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -249,6 +258,36 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN)); assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
} }
@Test
public void testReloadableResourceBundleMessageSourceWithCacheSeconds() throws InterruptedException {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages");
ms.setCacheSeconds(1);
// Initial cache attempt
assertEquals("message1", ms.getMessage("code1", null, Locale.ENGLISH));
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
Thread.sleep(1100);
// Late enough for a re-cache attempt
assertEquals("message1", ms.getMessage("code1", null, Locale.ENGLISH));
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
}
@Test
public void testReloadableResourceBundleMessageSourceWithNonConcurrentRefresh() throws InterruptedException {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages");
ms.setCacheSeconds(1);
ms.setConcurrentRefresh(false);
// Initial cache attempt
assertEquals("message1", ms.getMessage("code1", null, Locale.ENGLISH));
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
Thread.sleep(1100);
// Late enough for a re-cache attempt
assertEquals("message1", ms.getMessage("code1", null, Locale.ENGLISH));
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
}
@Test
public void testReloadableResourceBundleMessageSourceWithCommonMessages() { public void testReloadableResourceBundleMessageSourceWithCommonMessages() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
Properties commonMessages = new Properties(); Properties commonMessages = new Properties();
...@@ -261,6 +300,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -261,6 +300,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("Do not do that", ms.getMessage("warning", new Object[] {"that"}, Locale.GERMAN)); assertEquals("Do not do that", ms.getMessage("warning", new Object[] {"that"}, Locale.GERMAN));
} }
@Test
public void testReloadableResourceBundleMessageSourceWithWhitespaceInBasename() { public void testReloadableResourceBundleMessageSourceWithWhitespaceInBasename() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename(" org/springframework/context/support/messages "); ms.setBasename(" org/springframework/context/support/messages ");
...@@ -268,6 +308,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -268,6 +308,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN)); assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
} }
@Test
public void testReloadableResourceBundleMessageSourceWithDefaultCharset() { public void testReloadableResourceBundleMessageSourceWithDefaultCharset() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages"); ms.setBasename("org/springframework/context/support/messages");
...@@ -276,6 +317,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -276,6 +317,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN)); assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
} }
@Test
public void testReloadableResourceBundleMessageSourceWithInappropriateDefaultCharset() { public void testReloadableResourceBundleMessageSourceWithInappropriateDefaultCharset() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages"); ms.setBasename("org/springframework/context/support/messages");
...@@ -293,6 +335,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -293,6 +335,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
} }
} }
@Test
public void testReloadableResourceBundleMessageSourceWithInappropriateEnglishCharset() { public void testReloadableResourceBundleMessageSourceWithInappropriateEnglishCharset() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages"); ms.setBasename("org/springframework/context/support/messages");
...@@ -309,6 +352,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -309,6 +352,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
} }
} }
@Test
public void testReloadableResourceBundleMessageSourceWithInappropriateGermanCharset() { public void testReloadableResourceBundleMessageSourceWithInappropriateGermanCharset() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages"); ms.setBasename("org/springframework/context/support/messages");
...@@ -320,6 +364,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -320,6 +364,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("message2", ms.getMessage("code2", null, Locale.GERMAN)); assertEquals("message2", ms.getMessage("code2", null, Locale.GERMAN));
} }
@Test
public void testReloadableResourceBundleMessageSourceFileNameCalculation() { public void testReloadableResourceBundleMessageSourceFileNameCalculation() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
...@@ -352,6 +397,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -352,6 +397,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals(0, filenames.size()); assertEquals(0, filenames.size());
} }
@Test
public void testMessageSourceResourceBundle() { public void testMessageSourceResourceBundle() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource(); ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages"); ms.setBasename("org/springframework/context/support/messages");
...@@ -363,11 +409,10 @@ public class ResourceBundleMessageSourceTests extends TestCase { ...@@ -363,11 +409,10 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertTrue(rbg.containsKey("code2")); assertTrue(rbg.containsKey("code2"));
} }
@Override
protected void tearDown() throws Exception { @After
if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_16) { public void tearDown() {
ResourceBundle.clearCache(); ResourceBundle.clearCache();
}
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册