< prev index next >
src/java.base/share/classes/java/util/ResourceBundle.java
Print this page
@@ -377,11 +377,11 @@
}
@Override
public ResourceBundle getBundle(String baseName, Locale locale, Module module) {
// use the given module as the caller to bypass the access check
- return getBundleImpl(module, module, getLoader(module),
+ return getBundleImpl(module, module,
baseName, locale, Control.INSTANCE);
}
@Override
public ResourceBundle newResourceBundle(Class<? extends ResourceBundle> bundleClass) {
@@ -564,67 +564,23 @@
*/
public Locale getLocale() {
return locale;
}
- /*
- * Automatic determination of the ClassLoader to be used to load
- * resources on behalf of the client.
- */
- private static ClassLoader getLoader(Class<?> caller) {
- ClassLoader cl = caller == null ? null : caller.getClassLoader();
- if (cl == null) {
- // When the caller's loader is the boot class loader, cl is null
- // here. In that case, ClassLoader.getSystemClassLoader() may
- // return the same class loader that the application is
- // using. We therefore use a wrapper ClassLoader to create a
- // separate scope for bundles loaded on behalf of the Java
- // runtime so that these bundles cannot be returned from the
- // cache to the application (5048280).
- cl = RBClassLoader.INSTANCE;
- }
- return cl;
- }
-
private static ClassLoader getLoader(Module module) {
PrivilegedAction<ClassLoader> pa = module::getClassLoader;
return AccessController.doPrivileged(pa);
}
/**
- * A wrapper of ClassLoader.getSystemClassLoader().
+ * @param module a non-null-screened module form the {@link CacheKey#getModule()}.
+ * @return the ClassLoader to use in {@link Control#needsReload}
+ * and {@link Control#newBundle}
*/
- private static class RBClassLoader extends ClassLoader {
- private static final RBClassLoader INSTANCE = AccessController.doPrivileged(
- new PrivilegedAction<RBClassLoader>() {
- public RBClassLoader run() {
- return new RBClassLoader();
- }
- });
- private RBClassLoader() {
- }
- public Class<?> loadClass(String name) throws ClassNotFoundException {
- ClassLoader loader = ClassLoader.getSystemClassLoader();
- if (loader != null) {
- return loader.loadClass(name);
- }
- return Class.forName(name);
- }
- public URL getResource(String name) {
- ClassLoader loader = ClassLoader.getSystemClassLoader();
- if (loader != null) {
- return loader.getResource(name);
- }
- return ClassLoader.getSystemResource(name);
- }
- public InputStream getResourceAsStream(String name) {
- ClassLoader loader = ClassLoader.getSystemClassLoader();
- if (loader != null) {
- return loader.getResourceAsStream(name);
- }
- return ClassLoader.getSystemResourceAsStream(name);
- }
+ private static ClassLoader getLoaderForControl(Module module) {
+ ClassLoader loader = getLoader(module);
+ return loader == null ? ClassLoader.getSystemClassLoader() : loader;
}
/**
* Sets the parent bundle of this bundle.
* The parent bundle is searched by {@link #getObject getObject}
@@ -637,22 +593,23 @@
this.parent = parent;
}
/**
* Key used for cached resource bundles. The key checks the base
- * name, the locale, the class loader, and the caller module
+ * name, the locale, the bundle module, and the caller module
* to determine if the resource is a match to the requested one.
- * The loader may be null, but the base name, the locale and
- * module must have a non-null value.
+ * The base name, the locale and both modules must have a non-null value.
*/
private static class CacheKey implements Cloneable {
// These four are the actual keys for lookup in Map.
private String name;
private Locale locale;
- private KeyElementReference<ClassLoader> loaderRef;
private KeyElementReference<Module> moduleRef;
-
+ private KeyElementReference<Module> callerRef;
+ // this is the part of hashCode that pertains to module and callerModule
+ // which can be GCed..
+ private int modulesHash;
// bundle format which is necessary for calling
// Control.needsReload().
private String format;
@@ -667,65 +624,52 @@
private volatile long expirationTime;
// Placeholder for an error report by a Throwable
private Throwable cause;
- // Hash code value cache to avoid recalculating the hash code
- // of this instance.
- private int hashCodeCache;
-
// ResourceBundleProviders for loading ResourceBundles
private ServiceLoader<ResourceBundleProvider> providers;
private boolean providersChecked;
// Boolean.TRUE if the factory method caller provides a ResourceBundleProvier.
private Boolean callerHasProvider;
- CacheKey(String baseName, Locale locale, ClassLoader loader, Module module) {
+ CacheKey(String baseName, Locale locale, Module module, Module caller) {
Objects.requireNonNull(module);
+ Objects.requireNonNull(caller);
this.name = baseName;
this.locale = locale;
- if (loader == null) {
- this.loaderRef = null;
- } else {
- this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this);
- }
this.moduleRef = new KeyElementReference<>(module, referenceQueue, this);
- calculateHashCode();
+ this.callerRef = new KeyElementReference<>(caller, referenceQueue, this);
+ this.modulesHash = module.hashCode() ^ caller.hashCode();
}
String getName() {
return name;
}
CacheKey setName(String baseName) {
- if (!this.name.equals(baseName)) {
this.name = baseName;
- calculateHashCode();
- }
return this;
}
Locale getLocale() {
return locale;
}
CacheKey setLocale(Locale locale) {
- if (!this.locale.equals(locale)) {
this.locale = locale;
- calculateHashCode();
- }
return this;
}
- ClassLoader getLoader() {
- return (loaderRef != null) ? loaderRef.get() : null;
+ Module getModule() {
+ return moduleRef == null ? null : moduleRef.get();
}
- Module getModule() {
- return moduleRef.get();
+ Module getCallerModule() {
+ return callerRef == null ? null : callerRef.get();
}
ServiceLoader<ResourceBundleProvider> getProviders() {
if (!providersChecked) {
providers = getServiceLoader(getModule(), name);
@@ -748,68 +692,55 @@
return true;
}
try {
final CacheKey otherEntry = (CacheKey)other;
//quick check to see if they are not equal
- if (hashCodeCache != otherEntry.hashCodeCache) {
+ if (modulesHash != otherEntry.modulesHash) {
return false;
}
//are the names the same?
if (!name.equals(otherEntry.name)) {
return false;
}
// are the locales the same?
if (!locale.equals(otherEntry.locale)) {
return false;
}
- //are refs (both non-null) or (both null)?
- if (loaderRef == null) {
- return otherEntry.loaderRef == null;
- }
- ClassLoader loader = getLoader();
+ // are modules and callerModules the same and non-null?
Module module = getModule();
- return (otherEntry.loaderRef != null)
- // with a null reference we can no longer find
- // out which class loader or module was referenced; so
- // treat it as unequal
- && (loader != null)
- && (loader == otherEntry.getLoader())
- && (module != null)
- && (module.equals(otherEntry.getModule()));
+ Module caller = getCallerModule();
+ return ((module != null) && (module.equals(otherEntry.getModule())) &&
+ (caller != null) && (caller.equals(otherEntry.getCallerModule())));
} catch (NullPointerException | ClassCastException e) {
}
return false;
}
@Override
public int hashCode() {
- return hashCodeCache;
- }
-
- private void calculateHashCode() {
- hashCodeCache = name.hashCode() << 3;
- hashCodeCache ^= locale.hashCode();
- ClassLoader loader = getLoader();
- if (loader != null) {
- hashCodeCache ^= loader.hashCode();
- }
- Module module = getModule();
- if (module != null) {
- hashCodeCache ^= module.hashCode();
- }
+ return (name.hashCode() << 3) ^ locale.hashCode() ^ modulesHash;
}
@Override
public Object clone() {
try {
CacheKey clone = (CacheKey) super.clone();
- if (loaderRef != null) {
- clone.loaderRef = new KeyElementReference<>(getLoader(),
- referenceQueue, clone);
- }
- clone.moduleRef = new KeyElementReference<>(getModule(),
- referenceQueue, clone);
+
+ Module module = getModule();
+ clone.moduleRef =
+ module == null
+ ? null // don't ever create a Reference for a null referent
+ // because it will never be enqueued and consequently
+ // its CacheKey will never be removed from cacheList...
+ : new KeyElementReference<>(module, referenceQueue, clone);
+
+ Module caller = getCallerModule();
+ clone.callerRef =
+ caller == null
+ ? null
+ : new KeyElementReference<>(caller, referenceQueue, clone);
+
// Clear the reference to ResourceBundleProviders and the flag
clone.providers = null;
clone.providersChecked = false;
// Clear the reference to a Throwable
clone.cause = null;
@@ -854,12 +785,16 @@
l = "__" + locale.getVariant();
} else {
l = "\"\"";
}
}
- return "CacheKey[" + name + ", lc=" + l + ", ldr=" + getLoader()
- + "(format=" + format + ")]";
+ return "CacheKey[" + name +
+ ", locale=" + l +
+ ", module=" + getModule() +
+ ", callerModule=" + getCallerModule() +
+ ", format=" + format +
+ "]";
}
}
/**
* The common interface to get a CacheKey in LoaderReference and
@@ -1592,11 +1527,11 @@
private static ResourceBundle getBundleImpl(String baseName,
Locale locale,
Class<?> caller,
Control control) {
- return getBundleImpl(baseName, locale, caller, getLoader(caller), control);
+ return getBundleImpl(baseName, locale, caller, caller.getClassLoader(), control);
}
/**
* This method will find resource bundles using the legacy mechanism
* if the caller is unnamed module or the given class loader is
@@ -1611,30 +1546,29 @@
private static ResourceBundle getBundleImpl(String baseName,
Locale locale,
Class<?> caller,
ClassLoader loader,
Control control) {
- if (caller != null && caller.getModule().isNamed()) {
- Module module = caller.getModule();
- ClassLoader ml = getLoader(module);
- // get resource bundles for a named module only
- // if loader is the module's class loader
- if (loader == ml || (ml == null && loader == RBClassLoader.INSTANCE)) {
- return getBundleImpl(module, module, loader, baseName, locale, control);
+ if (caller == null) {
+ throw new InternalError("null caller");
}
+ Module callerModule = caller.getModule();
+
+ // get resource bundles for a named module only if loader is the module's class loader
+ if (callerModule.isNamed() && loader == getLoader(callerModule)) {
+ return getBundleImpl(callerModule, callerModule, baseName, locale, control);
}
- // find resource bundles from unnamed module
- Module unnamedModule = loader != null
- ? loader.getUnnamedModule()
- : ClassLoader.getSystemClassLoader().getUnnamedModule();
- if (caller == null) {
- throw new InternalError("null caller");
+ // there's no code in unnamed module of bootstrap class loader so loader
+ // must be non-null (non-bootstrap) if the caller is from unnamed module
+ if (loader == null) {
+ throw new InternalError("null loader");
}
- Module callerModule = caller.getModule();
- return getBundleImpl(callerModule, unnamedModule, loader, baseName, locale, control);
+ // find resource bundles from unnamed module of given class loader
+ Module unnamedModule = loader.getUnnamedModule();
+ return getBundleImpl(callerModule, unnamedModule, baseName, locale, control);
}
private static ResourceBundle getBundleFromModule(Class<?> caller,
Module module,
String baseName,
@@ -1646,16 +1580,15 @@
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(GET_CLASSLOADER_PERMISSION);
}
}
- return getBundleImpl(callerModule, module, getLoader(module), baseName, locale, control);
+ return getBundleImpl(callerModule, module, baseName, locale, control);
}
private static ResourceBundle getBundleImpl(Module callerModule,
Module module,
- ClassLoader loader,
String baseName,
Locale locale,
Control control) {
if (locale == null || control == null) {
throw new NullPointerException();
@@ -1663,11 +1596,11 @@
// We create a CacheKey here for use by this call. The base name
// loader, and module will never change during the bundle loading
// process. We have to make sure that the locale is set before
// using it as a cache key.
- CacheKey cacheKey = new CacheKey(baseName, locale, loader, module);
+ CacheKey cacheKey = new CacheKey(baseName, locale, module, callerModule);
ResourceBundle bundle = null;
// Quick lookup of the cache.
BundleReference bundleRef = cacheList.get(cacheKey);
if (bundleRef != null) {
@@ -2015,16 +1948,24 @@
// Here we actually load the bundle in the order of formats
// specified by the getFormats() value.
Locale targetLocale = cacheKey.getLocale();
+ Module module = cacheKey.getModule();
+ if (module == null) {
+ // should not happen
+ throw new InternalError(
+ "Module for cache key: " + cacheKey + " has been GCed.");
+ }
+ ClassLoader loader = getLoaderForControl(module);
+
ResourceBundle bundle = null;
for (String format : formats) {
try {
// ResourceBundle.Control.newBundle may be overridden
bundle = control.newBundle(cacheKey.getName(), targetLocale, format,
- cacheKey.getLoader(), reload);
+ loader, reload);
} catch (LinkageError | Exception error) {
// We need to handle the LinkageError case due to
// inconsistent case-sensitivity in ClassLoader.
// See 6572242 for details.
cacheKey.setCause(error);
@@ -2162,14 +2103,17 @@
synchronized (bundle) {
expirationTime = key.expirationTime;
if (!bundle.expired && expirationTime >= 0 &&
expirationTime <= System.currentTimeMillis()) {
try {
- bundle.expired = control.needsReload(key.getName(),
+ Module module = cacheKey.getModule();
+ bundle.expired =
+ module == null || // already GCed
+ control.needsReload(key.getName(),
key.getLocale(),
key.getFormat(),
- key.getLoader(),
+ getLoaderForControl(module),
bundle,
key.loadTime);
} catch (Exception e) {
cacheKey.setCause(e);
}
@@ -2263,11 +2207,11 @@
* @see ResourceBundle.Control#getTimeToLive(String,Locale)
*/
@CallerSensitive
public static final void clearCache() {
Class<?> caller = Reflection.getCallerClass();
- clearCache(getLoader(caller), caller.getModule());
+ clearCacheImpl(caller.getModule(), caller.getClassLoader());
}
/**
* Removes all resource bundles from the cache that have been loaded
* by the caller's module using the given class loader.
@@ -2278,11 +2222,12 @@
* @see ResourceBundle.Control#getTimeToLive(String,Locale)
*/
@CallerSensitive
public static final void clearCache(ClassLoader loader) {
Objects.requireNonNull(loader);
- clearCache(loader, Reflection.getCallerClass().getModule());
+ Class<?> caller = Reflection.getCallerClass();
+ clearCacheImpl(caller.getModule(), loader);
}
/**
* Removes all resource bundles from the cache that have been loaded by the
* given {@code module}.
@@ -2296,17 +2241,19 @@
* of the given {@code module}
* @since 9
* @see ResourceBundle.Control#getTimeToLive(String,Locale)
*/
public static final void clearCache(Module module) {
- clearCache(module.getClassLoader(), module);
+ Objects.requireNonNull(module);
+ clearCacheImpl(module, module.getClassLoader());
}
- private static void clearCache(ClassLoader loader, Module module) {
- Set<CacheKey> set = cacheList.keySet();
- set.stream().filter((key) -> (key.getLoader() == loader && key.getModule() == module))
- .forEach(set::remove);
+ private static void clearCacheImpl(Module callerModule, ClassLoader loader) {
+ cacheList.keySet().removeIf(
+ key -> key.getCallerModule() == callerModule &&
+ getLoader(key.getModule()) == loader
+ );
}
/**
* Gets an object for the given key from this resource bundle.
* Returns null if this resource bundle does not contain an
< prev index next >