-
Bug
-
Resolution: Unresolved
-
P4
-
7u80, 8u60, 9
-
x86
-
windows_8
FULL PRODUCT VERSION :
A DESCRIPTION OF THE PROBLEM :
There seems to be a concurrency problem inside java.util.ResourceBundle.getBundle() caching logic where it's possible that the algorithm returns NONEXISTENT_BUNDLE, which is not supposed to be returned.
ResrouceBundle.putBundleInCache() could return NONEXISTENT_BUNDLE if another thread has put it in the cache. It should check the result of cacheList.putIfAbsent() to not be NONEXISTENT_BUNDLE
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.util.MissingResourceException: Can't find resource for bundle java.util.ResourceBundle$1, key NumberElements
at java.util.ResourceBundle.getObject(ResourceBundle.java:395) ~[na:1.7.0_67]
at java.util.ResourceBundle.getObject(ResourceBundle.java:392) ~[na:1.7.0_67]
at java.util.ResourceBundle.getStringArray(ResourceBundle.java:372) ~[na:1.7.0_67]
at java.text.DecimalFormatSymbols.initialize(DecimalFormatSymbols.java:541) ~[na:1.7.0_67]
at java.text.DecimalFormatSymbols.<init>(DecimalFormatSymbols.java:94) ~[na:1.7.0_67]
at java.text.DecimalFormatSymbols.getInstance(DecimalFormatSymbols.java:157) ~[na:1.7.0_67]
at java.util.Formatter.getZero(Formatter.java:2251) ~[na:1.7.0_67]
at java.util.Formatter.<init>(Formatter.java:1877) ~[na:1.7.0_67]
at java.util.Formatter.<init>(Formatter.java:1898) ~[na:1.7.0_67]
at java.lang.String.format(String.java:2790) ~[na:1.7.0_67]
REPRODUCIBILITY :
This bug can be reproduced rarely.
---------- BEGIN SOURCE ----------
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Random;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicInteger;
public class ResourceBundleTest {
static Random rnd = new Random();
static AtomicInteger ctr = new AtomicInteger();
static AtomicInteger tryCounter = new AtomicInteger();
public static void main(String[] args) throws Exception {
final CL1 cl = new CL1(new URL[0]);
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (rnd.nextFloat() > 0.8) {
ctr.incrementAndGet();
}
int c = tryCounter.incrementAndGet();
if (c % 1000 == 0) {
System.out.println(c);
}
try {
ResourceBundle bundle = ResourceBundle.getBundle("" + ctr.get(), Locale.getDefault(), cl);
if (bundle.getClass().getName().contains("$1")) {
System.err.println("ERROR: " + bundle + " " + c);
System.exit(1);
}
}
catch (MissingResourceException e) {
}
}
}
}).start();
}
}
static class CL1 extends URLClassLoader {
private Random r = new Random();
public CL1(URL[] urls) {
super(urls);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (r.nextBoolean()) {
return Bundle.class;
}
else {
throw new ClassNotFoundException();
}
}
}
public static class Bundle extends ResourceBundle {
@Override
protected Object handleGetObject(String key) {
return new Object();
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(Arrays.asList("foo"));
}
}
}
---------- END SOURCE ----------
A DESCRIPTION OF THE PROBLEM :
There seems to be a concurrency problem inside java.util.ResourceBundle.getBundle() caching logic where it's possible that the algorithm returns NONEXISTENT_BUNDLE, which is not supposed to be returned.
ResrouceBundle.putBundleInCache() could return NONEXISTENT_BUNDLE if another thread has put it in the cache. It should check the result of cacheList.putIfAbsent() to not be NONEXISTENT_BUNDLE
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.util.MissingResourceException: Can't find resource for bundle java.util.ResourceBundle$1, key NumberElements
at java.util.ResourceBundle.getObject(ResourceBundle.java:395) ~[na:1.7.0_67]
at java.util.ResourceBundle.getObject(ResourceBundle.java:392) ~[na:1.7.0_67]
at java.util.ResourceBundle.getStringArray(ResourceBundle.java:372) ~[na:1.7.0_67]
at java.text.DecimalFormatSymbols.initialize(DecimalFormatSymbols.java:541) ~[na:1.7.0_67]
at java.text.DecimalFormatSymbols.<init>(DecimalFormatSymbols.java:94) ~[na:1.7.0_67]
at java.text.DecimalFormatSymbols.getInstance(DecimalFormatSymbols.java:157) ~[na:1.7.0_67]
at java.util.Formatter.getZero(Formatter.java:2251) ~[na:1.7.0_67]
at java.util.Formatter.<init>(Formatter.java:1877) ~[na:1.7.0_67]
at java.util.Formatter.<init>(Formatter.java:1898) ~[na:1.7.0_67]
at java.lang.String.format(String.java:2790) ~[na:1.7.0_67]
REPRODUCIBILITY :
This bug can be reproduced rarely.
---------- BEGIN SOURCE ----------
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Random;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicInteger;
public class ResourceBundleTest {
static Random rnd = new Random();
static AtomicInteger ctr = new AtomicInteger();
static AtomicInteger tryCounter = new AtomicInteger();
public static void main(String[] args) throws Exception {
final CL1 cl = new CL1(new URL[0]);
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (rnd.nextFloat() > 0.8) {
ctr.incrementAndGet();
}
int c = tryCounter.incrementAndGet();
if (c % 1000 == 0) {
System.out.println(c);
}
try {
ResourceBundle bundle = ResourceBundle.getBundle("" + ctr.get(), Locale.getDefault(), cl);
if (bundle.getClass().getName().contains("$1")) {
System.err.println("ERROR: " + bundle + " " + c);
System.exit(1);
}
}
catch (MissingResourceException e) {
}
}
}
}).start();
}
}
static class CL1 extends URLClassLoader {
private Random r = new Random();
public CL1(URL[] urls) {
super(urls);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (r.nextBoolean()) {
return Bundle.class;
}
else {
throw new ClassNotFoundException();
}
}
}
public static class Bundle extends ResourceBundle {
@Override
protected Object handleGetObject(String key) {
return new Object();
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(Arrays.asList("foo"));
}
}
}
---------- END SOURCE ----------