Browse Source

ReloadableResourceBundleMessageSource uses ConcurrentHashMaps and ReentrantLocks instead of synchronization

Issue: SPR-10500
pull/633/merge
Juergen Hoeller 10 years ago
parent
commit
a000dd782a
  1. 230
      spring-context/src/main/java/org/springframework/context/support/ReloadableResourceBundleMessageSource.java
  2. 85
      spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java

230
spring-context/src/main/java/org/springframework/context/support/ReloadableResourceBundleMessageSource.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* 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");
* 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.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
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.core.io.DefaultResourceLoader;
@ -92,8 +94,7 @@ import org.springframework.util.StringUtils; @@ -92,8 +94,7 @@ import org.springframework.util.StringUtils;
* @see ResourceBundleMessageSource
* @see java.util.ResourceBundle
*/
public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
implements ResourceLoaderAware {
public class ReloadableResourceBundleMessageSource extends AbstractMessageSource implements ResourceLoaderAware {
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 boolean concurrentRefresh = true;
private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
private ResourceLoader resourceLoader = new DefaultResourceLoader();
/** Cache to hold filename lists per Locale */
private final Map<String, Map<Locale, List<String>>> cachedFilenames =
new HashMap<String, Map<Locale, List<String>>>();
private final ConcurrentMap<String, Map<Locale, List<String>>> cachedFilenames =
new ConcurrentHashMap<String, Map<Locale, List<String>>>();
/** 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 */
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);
}
/**
* 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.
* <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.
*/
protected PropertiesHolder getMergedProperties(Locale locale) {
synchronized (this.cachedMergedProperties) {
PropertiesHolder mergedHolder = this.cachedMergedProperties.get(locale);
if (mergedHolder != null) {
return mergedHolder;
}
Properties mergedProps = new Properties();
mergedHolder = new PropertiesHolder(mergedProps, -1);
for (int i = this.basenames.length - 1; i >= 0; i--) {
List<String> filenames = calculateAllFilenames(this.basenames[i], locale);
for (int j = filenames.size() - 1; j >= 0; j--) {
String filename = filenames.get(j);
PropertiesHolder propHolder = getProperties(filename);
if (propHolder.getProperties() != null) {
mergedProps.putAll(propHolder.getProperties());
}
PropertiesHolder mergedHolder = this.cachedMergedProperties.get(locale);
if (mergedHolder != null) {
return mergedHolder;
}
Properties mergedProps = new Properties();
mergedHolder = new PropertiesHolder(mergedProps, -1);
for (int i = this.basenames.length - 1; i >= 0; i--) {
List<String> filenames = calculateAllFilenames(this.basenames[i], locale);
for (int j = filenames.size() - 1; j >= 0; j--) {
String filename = filenames.get(j);
PropertiesHolder propHolder = getProperties(filename);
if (propHolder.getProperties() != null) {
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
*/
protected List<String> calculateAllFilenames(String basename, Locale locale) {
synchronized (this.cachedFilenames) {
Map<Locale, List<String>> localeMap = this.cachedFilenames.get(basename);
if (localeMap != null) {
List<String> filenames = localeMap.get(locale);
if (filenames != null) {
return filenames;
}
Map<Locale, List<String>> localeMap = this.cachedFilenames.get(basename);
if (localeMap != null) {
List<String> filenames = localeMap.get(locale);
if (filenames != null) {
return filenames;
}
List<String> filenames = new ArrayList<String>(7);
filenames.addAll(calculateFilenamesForLocale(basename, locale));
if (this.fallbackToSystemLocale && !locale.equals(Locale.getDefault())) {
List<String> fallbackFilenames = calculateFilenamesForLocale(basename, Locale.getDefault());
for (String fallbackFilename : fallbackFilenames) {
if (!filenames.contains(fallbackFilename)) {
// Entry for fallback locale that isn't already in filenames list.
filenames.add(fallbackFilename);
}
}
List<String> filenames = new ArrayList<String>(7);
filenames.addAll(calculateFilenamesForLocale(basename, locale));
if (this.fallbackToSystemLocale && !locale.equals(Locale.getDefault())) {
List<String> fallbackFilenames = calculateFilenamesForLocale(basename, Locale.getDefault());
for (String fallbackFilename : fallbackFilenames) {
if (!filenames.contains(fallbackFilename)) {
// Entry for fallback locale that isn't already in filenames list.
filenames.add(fallbackFilename);
}
}
filenames.add(basename);
if (localeMap != null) {
localeMap.put(locale, filenames);
}
else {
localeMap = new HashMap<Locale, List<String>>();
localeMap.put(locale, filenames);
this.cachedFilenames.put(basename, localeMap);
}
filenames.add(basename);
if (localeMap == null) {
localeMap = new ConcurrentHashMap<Locale, List<String>>();
Map<Locale, List<String>> existing = this.cachedFilenames.putIfAbsent(basename, localeMap);
if (existing != null) {
localeMap = existing;
}
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
*/
protected PropertiesHolder getProperties(String filename) {
synchronized (this.cachedProperties) {
PropertiesHolder propHolder = this.cachedProperties.get(filename);
if (propHolder != null &&
(propHolder.getRefreshTimestamp() < 0 ||
propHolder.getRefreshTimestamp() > System.currentTimeMillis() - this.cacheMillis)) {
// up to date
PropertiesHolder propHolder = this.cachedProperties.get(filename);
long originalTimestamp = -1;
if (propHolder != null) {
originalTimestamp = propHolder.getRefreshTimestamp();
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;
}
}
else {
propHolder.refreshLock.lock();
}
try {
PropertiesHolder existingHolder = this.cachedProperties.get(filename);
if (existingHolder != null && existingHolder.getRefreshTimestamp() > originalTimestamp) {
return existingHolder;
}
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) {
// Probably a class path resource: cache it forever.
if (logger.isDebugEnabled()) {
logger.debug(
resource + " could not be resolved in the file system - assuming that is hasn't changed", ex);
logger.debug(resource + " could not be resolved in the file system - assuming that it hasn't changed", ex);
}
fileTimestamp = -1;
}
@ -561,12 +608,8 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource @@ -561,12 +608,8 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
*/
public void clearCache() {
logger.debug("Clearing entire resource bundle cache");
synchronized (this.cachedProperties) {
this.cachedProperties.clear();
}
synchronized (this.cachedMergedProperties) {
this.cachedMergedProperties.clear();
}
this.cachedProperties.clear();
this.cachedMergedProperties.clear();
}
/**
@ -595,30 +638,34 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource @@ -595,30 +638,34 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
*/
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 */
private final Map<String, Map<Locale, MessageFormat>> cachedMessageFormats =
new HashMap<String, Map<Locale, MessageFormat>>();
private final ConcurrentMap<String, Map<Locale, MessageFormat>> cachedMessageFormats =
new ConcurrentHashMap<String, Map<Locale, MessageFormat>>();
public PropertiesHolder() {
this.properties = null;
this.fileTimestamp = -1;
}
public PropertiesHolder(Properties properties, long fileTimestamp) {
this.properties = properties;
this.fileTimestamp = fileTimestamp;
}
public PropertiesHolder() {
}
public Properties getProperties() {
return properties;
return this.properties;
}
public long getFileTimestamp() {
return fileTimestamp;
return this.fileTimestamp;
}
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() {
return refreshTimestamp;
return this.refreshTimestamp;
}
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) {
return null;
}
synchronized (this.cachedMessageFormats) {
Map<Locale, MessageFormat> localeMap = this.cachedMessageFormats.get(code);
if (localeMap != null) {
MessageFormat result = localeMap.get(locale);
if (result != null) {
return result;
}
Map<Locale, MessageFormat> localeMap = this.cachedMessageFormats.get(code);
if (localeMap != null) {
MessageFormat result = localeMap.get(locale);
if (result != null) {
return result;
}
String msg = this.properties.getProperty(code);
if (msg != null) {
if (localeMap == null) {
localeMap = new HashMap<Locale, MessageFormat>();
this.cachedMessageFormats.put(code, localeMap);
}
String msg = this.properties.getProperty(code);
if (msg != null) {
if (localeMap == null) {
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;
}
}

85
spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* 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");
* 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.ResourceBundle;
import junit.framework.TestCase;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.JdkVersion;
import static org.junit.Assert.*;
/**
* @author Juergen Hoeller
* @since 03.02.2004
*/
public class ResourceBundleMessageSourceTests extends TestCase {
public class ResourceBundleMessageSourceTests {
@Test
public void testMessageAccessWithDefaultMessageSource() {
doTestMessageAccess(false, true, false, false, false);
}
@Test
public void testMessageAccessWithDefaultMessageSourceAndMessageFormat() {
doTestMessageAccess(false, true, false, false, true);
}
@Test
public void testMessageAccessWithDefaultMessageSourceAndFallbackToGerman() {
doTestMessageAccess(false, true, true, true, false);
}
@Test
public void testMessageAccessWithDefaultMessageSourceAndFallbackTurnedOff() {
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
return;
}
doTestMessageAccess(false, false, false, false, false);
}
@Test
public void testMessageAccessWithDefaultMessageSourceAndFallbackTurnedOffAndFallbackToGerman() {
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
return;
}
doTestMessageAccess(false, false, true, true, false);
}
@Test
public void testMessageAccessWithReloadableMessageSource() {
doTestMessageAccess(true, true, false, false, false);
}
@Test
public void testMessageAccessWithReloadableMessageSourceAndMessageFormat() {
doTestMessageAccess(true, true, false, false, true);
}
@Test
public void testMessageAccessWithReloadableMessageSourceAndFallbackToGerman() {
doTestMessageAccess(true, true, true, true, false);
}
@Test
public void testMessageAccessWithReloadableMessageSourceAndFallbackTurnedOff() {
doTestMessageAccess(true, false, false, false, false);
}
@Test
public void testMessageAccessWithReloadableMessageSourceAndFallbackTurnedOffAndFallbackToGerman() {
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();
String basepath = "org/springframework/context/support/";
String[] basenames = null;
String[] basenames;
if (reloadable) {
basenames = new String[] {
"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("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));
}
@ -196,6 +202,7 @@ public class ResourceBundleMessageSourceTests extends TestCase { @@ -196,6 +202,7 @@ public class ResourceBundleMessageSourceTests extends TestCase {
}
}
@Test
public void testDefaultApplicationContextMessageSource() {
GenericApplicationContext ac = new GenericApplicationContext();
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));
}
@Test
public void testResourceBundleMessageSourceStandalone() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
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));
}
@Test
public void testResourceBundleMessageSourceWithWhitespaceInBasename() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
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));
}
@Test
public void testResourceBundleMessageSourceWithDefaultCharset() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
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));
}
@Test
public void testResourceBundleMessageSourceWithInappropriateDefaultCharset() {
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
return;
}
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages");
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() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
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));
}
@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() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
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));
}
@Test
public void testReloadableResourceBundleMessageSourceWithWhitespaceInBasename() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
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));
}
@Test
public void testReloadableResourceBundleMessageSourceWithDefaultCharset() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
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));
}
@Test
public void testReloadableResourceBundleMessageSourceWithInappropriateDefaultCharset() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
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() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
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() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
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));
}
@Test
public void testReloadableResourceBundleMessageSourceFileNameCalculation() {
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());
}
@Test
public void testMessageSourceResourceBundle() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
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"));
}
@Override
protected void tearDown() throws Exception {
if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_16) {
ResourceBundle.clearCache();
}
@After
public void tearDown() {
ResourceBundle.clearCache();
}
}

Loading…
Cancel
Save