< prev index next >

src/java.base/share/classes/java/util/ResourceBundle.java

Print this page

        

*** 42,52 **** import java.io.IOException; import java.io.InputStream; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; - import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.lang.reflect.Module; import java.net.JarURLConnection; --- 42,51 ----
*** 54,73 **** import java.net.URLConnection; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; - import java.util.concurrent.ConcurrentHashMap; - import java.util.concurrent.ConcurrentMap; import java.util.jar.JarEntry; import java.util.spi.ResourceBundleControlProvider; import java.util.spi.ResourceBundleProvider; import jdk.internal.misc.JavaUtilResourceBundleAccess; import jdk.internal.misc.SharedSecrets; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import sun.util.locale.BaseLocale; import sun.util.locale.LocaleObjectCache; import sun.util.locale.provider.ResourceBundleProviderSupport; import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION; --- 53,72 ---- import java.net.URLConnection; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.jar.JarEntry; import java.util.spi.ResourceBundleControlProvider; import java.util.spi.ResourceBundleProvider; import jdk.internal.misc.JavaUtilResourceBundleAccess; import jdk.internal.misc.SharedSecrets; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; + import jdk.internal.util.concurrent.AbstractClassLoaderValue; + import jdk.internal.util.concurrent.ClassLoaderValue; import sun.util.locale.BaseLocale; import sun.util.locale.LocaleObjectCache; import sun.util.locale.provider.ResourceBundleProviderSupport; import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
*** 344,356 **** * @see ResourceBundleProvider * @since 1.1 */ public abstract class ResourceBundle { - /** initial size of the bundle cache */ - private static final int INITIAL_CACHE_SIZE = 32; - static { SharedSecrets.setJavaUtilResourceBundleAccess( new JavaUtilResourceBundleAccess() { @Override public void setParent(ResourceBundle bundle, --- 343,352 ----
*** 373,404 **** bundle.name = name; } }); } ! /** constant indicating that no resource bundle exists */ ! private static final ResourceBundle NONEXISTENT_BUNDLE = new ResourceBundle() { public Enumeration<String> getKeys() { return null; } protected Object handleGetObject(String key) { return null; } public String toString() { return "NONEXISTENT_BUNDLE"; } ! }; /** ! * The cache is a map from cache keys (with bundle base name, locale, and ! * class loader) to either a resource bundle or NONEXISTENT_BUNDLE wrapped by a ! * BundleReference. ! * ! * The cache is a ConcurrentMap, allowing the cache to be searched ! * concurrently by multiple threads. This will also allow the cache keys ! * to be reclaimed along with the ClassLoaders they reference. ! * ! * This variable would be better named "cache", but we keep the old ! * name for compatibility with some workarounds for bug 4212439. */ ! private static final ConcurrentMap<CacheKey, BundleReference> cacheList ! = new ConcurrentHashMap<>(INITIAL_CACHE_SIZE); /** * Queue for reference objects referring to class loaders or bundles. */ private static final ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); --- 369,394 ---- bundle.name = name; } }); } ! /** ! * special bundle type indicating that no resource bundle exists ! */ ! private static final class NONEXISTENT_BUNDLE extends ResourceBundle { public Enumeration<String> getKeys() { return null; } protected Object handleGetObject(String key) { return null; } public String toString() { return "NONEXISTENT_BUNDLE"; } ! } /** ! * The cache of BundleReference(s) per class loader, module, bundle base name ! * and locale. */ ! private static final ClassLoaderValue<BundleReference> cache ! = new ClassLoaderValue<>(); /** * Queue for reference objects referring to class loaders or bundles. */ private static final ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
*** 424,455 **** /** * The parent bundle of this bundle. * The parent bundle is searched by {@link #getObject getObject} * when this bundle does not contain a particular resource. */ ! protected ResourceBundle parent = null; /** * The locale for this bundle. */ ! private Locale locale = null; /** * The base bundle name for this bundle. */ private String name; /** * The flag indicating this bundle has expired in the cache. */ private volatile boolean expired; /** ! * The back link to the cache key. null if this bundle isn't in ! * the cache (yet) or has expired. */ ! private volatile CacheKey cacheKey; /** * A Set of the keys contained only in this ResourceBundle. */ private volatile Set<String> keySet; --- 414,456 ---- /** * The parent bundle of this bundle. * The parent bundle is searched by {@link #getObject getObject} * when this bundle does not contain a particular resource. */ ! protected ResourceBundle parent; /** * The locale for this bundle. */ ! private Locale locale; /** * The base bundle name for this bundle. */ private String name; /** + * Bundle format which is necessary for calling + * Control.needsReload(). + */ + private String format; + + /** * The flag indicating this bundle has expired in the cache. */ private volatile boolean expired; /** ! * The time when the bundle has been loaded */ ! private volatile long loadTime; ! ! /** ! * The time when the bundle expires in the cache, or either ! * Control.TTL_DONT_CACHE or Control.TTL_NO_EXPIRATION_CONTROL. ! */ ! private volatile long expirationTime; /** * A Set of the keys contained only in this ResourceBundle. */ private volatile Set<String> keySet;
*** 616,822 **** * when this bundle does not contain a particular resource. * * @param parent this bundle's parent bundle. */ protected void setParent(ResourceBundle parent) { ! assert parent != NONEXISTENT_BUNDLE; this.parent = parent; } /** ! * Key used for cached resource bundles. The key checks the base ! * name, the locale, the class loader, 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. ! */ ! 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; - - - // bundle format which is necessary for calling - // Control.needsReload(). - private String format; - - // These time values are in CacheKey so that NONEXISTENT_BUNDLE - // doesn't need to be cloned for caching. ! // The time when the bundle has been loaded ! private volatile long loadTime; ! // The time when the bundle expires in the cache, or either ! // Control.TTL_DONT_CACHE or Control.TTL_NO_EXPIRATION_CONTROL. ! 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) { ! Objects.requireNonNull(module); ! ! 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(); } 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.get(); } ! ServiceLoader<ResourceBundleProvider> getProviders() { ! if (!providersChecked) { ! providers = getServiceLoader(getModule(), name); ! providersChecked = true; } return providers; } - boolean hasProviders() { - return getProviders() != null; - } - boolean callerHasProvider() { return callerHasProvider == Boolean.TRUE; } - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - try { - final CacheKey otherEntry = (CacheKey)other; - //quick check to see if they are not equal - if (hashCodeCache != otherEntry.hashCodeCache) { - 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(); - 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())); - } 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(); - } - } - - @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); - // Clear the reference to ResourceBundleProviders and the flag - clone.providers = null; - clone.providersChecked = false; - // Clear the reference to a Throwable - clone.cause = null; - // Clear callerHasProvider - clone.callerHasProvider = null; - return clone; - } catch (CloneNotSupportedException e) { - //this should never happen - throw new InternalError(e); - } - } - - String getFormat() { - return format; - } - - void setFormat(String format) { - this.format = format; - } - private void setCause(Throwable cause) { if (this.cause == null) { this.cause = cause; } else { // Override the cause if the previous one is --- 617,706 ---- * when this bundle does not contain a particular resource. * * @param parent this bundle's parent bundle. */ protected void setParent(ResourceBundle parent) { ! assert !(parent instanceof NONEXISTENT_BUNDLE); this.parent = parent; } /** ! * A session object used during the {@link #getBundle} call. ! * The loader may be null (in which case it is considered to be the ! * bootstrap class loader), but the module, base name, and locale must have ! * a non-null value. ! */ ! private static class LoadSession { ! // These four are the actual keys for lookup in cache. ! private ClassLoader loader; ! private Module module; private String name; private Locale locale; ! // compose and return the cache key ! ClassLoaderValue<BundleReference>.Sub<Module>.Sub<String>.Sub<Locale> key() { ! return cache.sub(module).sub(name).sub(locale); ! } ! // return a bundle reference from cache or null ! BundleReference getFromCache() { ! return key().get(loader); ! } // Placeholder for an error report by a Throwable private Throwable cause; // ResourceBundleProviders for loading ResourceBundles ! private Iterable<ResourceBundleProvider> providers; // Boolean.TRUE if the factory method caller provides a ResourceBundleProvier. private Boolean callerHasProvider; ! LoadSession(String baseName, Locale locale, ClassLoader loader, Module module) { ! this.name = Objects.requireNonNull(baseName); ! this.locale = Objects.requireNonNull(locale); ! this.loader = loader; ! this.module = Objects.requireNonNull(module); } String getName() { return name; } ! LoadSession setName(String baseName) { ! this.name = Objects.requireNonNull(baseName); return this; } Locale getLocale() { return locale; } ! LoadSession setLocale(Locale locale) { ! this.locale = Objects.requireNonNull(locale); return this; } ClassLoader getLoader() { ! return loader; } Module getModule() { ! return module; } ! Iterable<ResourceBundleProvider> getProviders() { ! if (providers == null) { ! providers = ResourceBundle.getProviders(module, name); } return providers; } boolean callerHasProvider() { return callerHasProvider == Boolean.TRUE; } private void setCause(Throwable cause) { if (this.cause == null) { this.cause = cause; } else { // Override the cause if the previous one is
*** 839,896 **** l = "__" + locale.getVariant(); } else { l = "\"\""; } } ! return "CacheKey[" + name + ", lc=" + l + ", ldr=" + getLoader() ! + "(format=" + format + ")]"; } } /** ! * The common interface to get a CacheKey in LoaderReference and ! * BundleReference. ! */ ! private static interface CacheKeyReference { ! public CacheKey getCacheKey(); ! } ! ! /** ! * References to a CacheKey element as a WeakReference so that it can be ! * garbage collected when nobody else is using it. */ ! private static class KeyElementReference<T> extends WeakReference<T> ! implements CacheKeyReference { ! private final CacheKey cacheKey; ! KeyElementReference(T referent, ReferenceQueue<Object> q, CacheKey key) { super(referent, q); ! cacheKey = key; } ! @Override ! public CacheKey getCacheKey() { ! return cacheKey; ! } ! } ! ! /** ! * References to bundles are soft references so that they can be garbage ! * collected when they have no hard references. ! */ ! private static class BundleReference extends SoftReference<ResourceBundle> ! implements CacheKeyReference { ! private final CacheKey cacheKey; ! ! BundleReference(ResourceBundle referent, ReferenceQueue<Object> q, CacheKey key) { ! super(referent, q); ! cacheKey = key; ! } ! ! @Override ! public CacheKey getCacheKey() { ! return cacheKey; } } /** * Gets a resource bundle using the specified base name, the default locale, --- 723,755 ---- l = "__" + locale.getVariant(); } else { l = "\"\""; } } ! return "LookupSession[" + name + ", lc=" + l + ", ldr=" + getLoader() ! + "]"; } } /** ! * References to bundles are soft references so that they can be cleared ! * when GC demands to free some heap. */ ! private static class BundleReference extends SoftReference<ResourceBundle> { ! private final ClassLoader classLoader; ! private final AbstractClassLoaderValue<?, BundleReference>.Sub<?> key; ! BundleReference(ResourceBundle referent, ReferenceQueue<Object> q, ! ClassLoader classLoader, ! AbstractClassLoaderValue<?, BundleReference>.Sub<?> key) { super(referent, q); ! this.classLoader = classLoader; ! this.key = key; } ! void remove() { ! key.remove(classLoader, this); } } /** * Gets a resource bundle using the specified base name, the default locale,
*** 1618,1639 **** Control control) { if (locale == null || control == null) { throw new NullPointerException(); } ! // 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); ! ResourceBundle bundle = null; // Quick lookup of the cache. ! BundleReference bundleRef = cacheList.get(cacheKey); if (bundleRef != null) { bundle = bundleRef.get(); - bundleRef = null; } // If this bundle and all of its parents are valid (not expired), // then return this bundle. If any of the bundles is expired, we // don't call control.needsReload here but instead drop into the --- 1477,1496 ---- Control control) { if (locale == null || control == null) { throw new NullPointerException(); } ! // We create a LookupSession here for use by this call. The base name // loader, and module will never change during the bundle loading ! // process. ! LoadSession loadSession = new LoadSession(baseName, locale, loader, module); // Quick lookup of the cache. ! ResourceBundle bundle = null; ! BundleReference bundleRef = loadSession.getFromCache(); if (bundleRef != null) { bundle = bundleRef.get(); } // If this bundle and all of its parents are valid (not expired), // then return this bundle. If any of the bundles is expired, we // don't call control.needsReload here but instead drop into the
*** 1659,1669 **** List<Locale> candidateLocales = control.getCandidateLocales(baseName, targetLocale); if (!isKnownControl && !checkList(candidateLocales)) { throw new IllegalArgumentException("Invalid Control: getCandidateLocales"); } ! bundle = findBundle(cacheKey, module, candidateLocales, formats, 0, control, baseBundle); // If the loaded bundle is the base bundle and exactly for the // requested locale or the only candidate locale, then take the // bundle as the resulting one. If the loaded bundle is the base // bundle, it's put on hold until we finish processing all --- 1516,1526 ---- List<Locale> candidateLocales = control.getCandidateLocales(baseName, targetLocale); if (!isKnownControl && !checkList(candidateLocales)) { throw new IllegalArgumentException("Invalid Control: getCandidateLocales"); } ! bundle = findBundle(loadSession, module, candidateLocales, formats, 0, control, baseBundle); // If the loaded bundle is the base bundle and exactly for the // requested locale or the only candidate locale, then take the // bundle as the resulting one. If the loaded bundle is the base // bundle, it's put on hold until we finish processing all
*** 1685,1695 **** } } if (bundle == null) { if (baseBundle == null) { ! throwMissingResourceException(baseName, locale, cacheKey.getCause()); } bundle = baseBundle; } return bundle; --- 1542,1552 ---- } } if (bundle == null) { if (baseBundle == null) { ! throwMissingResourceException(baseName, locale, loadSession.getCause()); } bundle = baseBundle; } return bundle;
*** 1708,1749 **** } } return valid; } ! private static ResourceBundle findBundle(CacheKey cacheKey, Module module, List<Locale> candidateLocales, List<String> formats, int index, Control control, ResourceBundle baseBundle) { Locale targetLocale = candidateLocales.get(index); ResourceBundle parent = null; if (index != candidateLocales.size() - 1) { ! parent = findBundle(cacheKey, module, candidateLocales, formats, index + 1, control, baseBundle); } else if (baseBundle != null && Locale.ROOT.equals(targetLocale)) { return baseBundle; } // Before we do the real loading work, see whether we need to ! // do some housekeeping: If references to class loaders or ! // resource bundles have been nulled out, remove all related ! // information from the cache. Object ref; while ((ref = referenceQueue.poll()) != null) { ! cacheList.remove(((CacheKeyReference)ref).getCacheKey()); } // flag indicating the resource bundle has expired in the cache boolean expiredBundle = false; // First, look up the cache to see if it's in the cache, without // attempting to load bundle. ! cacheKey.setLocale(targetLocale); ! ResourceBundle bundle = findBundleInCache(cacheKey, control); if (isValidBundle(bundle)) { expiredBundle = bundle.expired; if (!expiredBundle) { // If its parent is the one asked for by the candidate // locales (the runtime lookup path), we can take the cached --- 1565,1605 ---- } } return valid; } ! private static ResourceBundle findBundle(LoadSession loadSession, Module module, List<Locale> candidateLocales, List<String> formats, int index, Control control, ResourceBundle baseBundle) { Locale targetLocale = candidateLocales.get(index); ResourceBundle parent = null; if (index != candidateLocales.size() - 1) { ! parent = findBundle(loadSession, module, candidateLocales, formats, index + 1, control, baseBundle); } else if (baseBundle != null && Locale.ROOT.equals(targetLocale)) { return baseBundle; } // Before we do the real loading work, see whether we need to ! // do some housekeeping: If soft references to resource bundles ! // have been nulled out, remove all related information from the cache. Object ref; while ((ref = referenceQueue.poll()) != null) { ! ((BundleReference) ref).remove(); } // flag indicating the resource bundle has expired in the cache boolean expiredBundle = false; // First, look up the cache to see if it's in the cache, without // attempting to load bundle. ! loadSession.setLocale(targetLocale); ! ResourceBundle bundle = findBundleInCache(loadSession, control); if (isValidBundle(bundle)) { expiredBundle = bundle.expired; if (!expiredBundle) { // If its parent is the one asked for by the candidate // locales (the runtime lookup path), we can take the cached
*** 1753,1822 **** if (bundle.parent == parent) { return bundle; } // Otherwise, remove the cached one since we can't keep // the same bundles having different parents. ! BundleReference bundleRef = cacheList.get(cacheKey); if (bundleRef != null && bundleRef.get() == bundle) { ! cacheList.remove(cacheKey, bundleRef); } } } ! if (bundle != NONEXISTENT_BUNDLE) { ! CacheKey constKey = (CacheKey) cacheKey.clone(); ! ! try { if (module.isNamed()) { ! bundle = loadBundle(cacheKey, formats, control, module); } else { ! bundle = loadBundle(cacheKey, formats, control, expiredBundle); } if (bundle != null) { if (bundle.parent == null) { bundle.setParent(parent); } bundle.locale = targetLocale; ! bundle = putBundleInCache(cacheKey, bundle, control); return bundle; } // Put NONEXISTENT_BUNDLE in the cache as a mark that there's no bundle // instance for the locale. ! putBundleInCache(cacheKey, NONEXISTENT_BUNDLE, control); ! } finally { ! if (constKey.getCause() instanceof InterruptedException) { ! Thread.currentThread().interrupt(); ! } ! } } return parent; } private static final String UNKNOWN_FORMAT = ""; /* * Loads a ResourceBundle in named modules */ ! private static ResourceBundle loadBundle(CacheKey cacheKey, List<String> formats, Control control, Module module) { ! String baseName = cacheKey.getName(); ! Locale targetLocale = cacheKey.getLocale(); ResourceBundle bundle = null; ! if (cacheKey.hasProviders()) { bundle = loadBundleFromProviders(baseName, targetLocale, ! cacheKey.getProviders(), cacheKey); if (bundle != null) { ! cacheKey.setFormat(UNKNOWN_FORMAT); } } // If none of providers returned a bundle and the caller has no provider, // look up module-local bundles. ! if (bundle == null && !cacheKey.callerHasProvider()) { String bundleName = control.toBundleName(baseName, targetLocale); for (String format : formats) { try { switch (format) { case "java.class": --- 1609,1671 ---- if (bundle.parent == parent) { return bundle; } // Otherwise, remove the cached one since we can't keep // the same bundles having different parents. ! BundleReference bundleRef = loadSession.getFromCache(); if (bundleRef != null && bundleRef.get() == bundle) { ! bundleRef.remove(); } } } ! if (!(bundle instanceof NONEXISTENT_BUNDLE)) { if (module.isNamed()) { ! bundle = loadBundle(loadSession, formats, control, module); } else { ! bundle = loadBundle(loadSession, formats, control, expiredBundle); } if (bundle != null) { if (bundle.parent == null) { bundle.setParent(parent); } bundle.locale = targetLocale; ! bundle = putBundleInCache(loadSession, bundle, control); return bundle; } // Put NONEXISTENT_BUNDLE in the cache as a mark that there's no bundle // instance for the locale. ! putBundleInCache(loadSession, new NONEXISTENT_BUNDLE(), control); } return parent; } private static final String UNKNOWN_FORMAT = ""; /* * Loads a ResourceBundle in named modules */ ! private static ResourceBundle loadBundle(LoadSession loadSession, List<String> formats, Control control, Module module) { ! String baseName = loadSession.getName(); ! Locale targetLocale = loadSession.getLocale(); ResourceBundle bundle = null; ! Iterable<ResourceBundleProvider> providers = loadSession.getProviders(); ! if (providers != NO_PROVIDERS) { bundle = loadBundleFromProviders(baseName, targetLocale, ! providers, loadSession); if (bundle != null) { ! bundle.format = UNKNOWN_FORMAT; } } // If none of providers returned a bundle and the caller has no provider, // look up module-local bundles. ! if (bundle == null && !loadSession.callerHasProvider()) { String bundleName = control.toBundleName(baseName, targetLocale); for (String format : formats) { try { switch (format) { case "java.class":
*** 1831,1866 **** default: throw new InternalError("unexpected format: " + format); } if (bundle != null) { ! cacheKey.setFormat(format); break; } } catch (Exception e) { ! cacheKey.setCause(e); } } } return bundle; } ! private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module, String baseName) { if (!module.isNamed()) { ! return null; } PrivilegedAction<ClassLoader> pa = module::getClassLoader; ClassLoader loader = AccessController.doPrivileged(pa); ! return getServiceLoader(module, loader, baseName); } /** * Returns a ServiceLoader that will find providers that are bound to ! * a given module that may be named or unnamed. */ ! private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module, ClassLoader loader, String baseName) { // Look up <baseName> + "Provider" String providerName = baseName + "Provider"; --- 1680,1722 ---- default: throw new InternalError("unexpected format: " + format); } if (bundle != null) { ! bundle.format = format; break; } } catch (Exception e) { ! loadSession.setCause(e); } } } return bundle; } ! // An instance that signals getting providers for unnamed module or ! // inability to get providers as opposed to successfully getting 0 providers ! // from a named module. ! private static final Iterable<ResourceBundleProvider> NO_PROVIDERS = ! Collections.emptyList(); ! ! private static Iterable<ResourceBundleProvider> getProviders(Module module, String baseName) { if (!module.isNamed()) { ! return NO_PROVIDERS; } PrivilegedAction<ClassLoader> pa = module::getClassLoader; ClassLoader loader = AccessController.doPrivileged(pa); ! return getProviders(module, loader, baseName); } /** * Returns a ServiceLoader that will find providers that are bound to ! * a given module that may be named or unnamed or GET_PROVIDERS_FAILED instance ! * if unsuccessful. */ ! private static Iterable<ResourceBundleProvider> getProviders(Module module, ClassLoader loader, String baseName) { // Look up <baseName> + "Provider" String providerName = baseName + "Provider";
*** 1885,1968 **** if (service != null && Reflection.verifyModuleAccess(module, service)) { try { return ServiceLoader.load(service, loader, module); } catch (ServiceConfigurationError e) { // "uses" not declared: load bundle local in the module - return null; } } ! return null; } /** * Loads ResourceBundle from service providers. */ private static ResourceBundle loadBundleFromProviders(String baseName, Locale locale, ! ServiceLoader<ResourceBundleProvider> providers, ! CacheKey cacheKey) { ! if (providers == null) return null; ! return AccessController.doPrivileged( new PrivilegedAction<>() { public ResourceBundle run() { ! for (Iterator<ResourceBundleProvider> itr = providers.iterator(); itr.hasNext(); ) { try { ! ResourceBundleProvider provider = itr.next(); ! if (cacheKey != null && cacheKey.callerHasProvider == null ! && cacheKey.getModule() == provider.getClass().getModule()) { ! cacheKey.callerHasProvider = Boolean.TRUE; } ResourceBundle bundle = provider.getBundle(baseName, locale); if (bundle != null) { return bundle; } } catch (ServiceConfigurationError | SecurityException e) { ! if (cacheKey != null) { ! cacheKey.setCause(e); } } ! } ! if (cacheKey != null && cacheKey.callerHasProvider == null) { ! cacheKey.callerHasProvider = Boolean.FALSE; } return null; } }); } /* * Legacy mechanism to load resource bundles */ ! private static ResourceBundle loadBundle(CacheKey cacheKey, List<String> formats, Control control, boolean reload) { // Here we actually load the bundle in the order of formats // specified by the getFormats() value. ! Locale targetLocale = cacheKey.getLocale(); ResourceBundle bundle = null; for (String format : formats) { try { // ResourceBundle.Control.newBundle may be overridden ! bundle = control.newBundle(cacheKey.getName(), targetLocale, format, ! cacheKey.getLoader(), 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); } if (bundle != null) { // Set the format in the cache key so that it can be // used when calling needsReload later. ! cacheKey.setFormat(format); ! bundle.name = cacheKey.getName(); bundle.locale = targetLocale; // Bundle provider might reuse instances. So we should make // sure to clear the expired flag here. bundle.expired = false; break; --- 1741,1820 ---- if (service != null && Reflection.verifyModuleAccess(module, service)) { try { return ServiceLoader.load(service, loader, module); } catch (ServiceConfigurationError e) { // "uses" not declared: load bundle local in the module } } ! return NO_PROVIDERS; } /** * Loads ResourceBundle from service providers. */ private static ResourceBundle loadBundleFromProviders(String baseName, Locale locale, ! Iterable<ResourceBundleProvider> providers, ! LoadSession loadSession) { ! // assert loadSession != null && providers != null; return AccessController.doPrivileged( new PrivilegedAction<>() { public ResourceBundle run() { ! for (ResourceBundleProvider provider : providers) { try { ! if (loadSession.callerHasProvider == null && ! loadSession.getModule() == provider.getClass().getModule()) ! { ! loadSession.callerHasProvider = Boolean.TRUE; } ResourceBundle bundle = provider.getBundle(baseName, locale); if (bundle != null) { return bundle; } } catch (ServiceConfigurationError | SecurityException e) { ! loadSession.setCause(e); } } ! if (loadSession.callerHasProvider == null) { ! loadSession.callerHasProvider = Boolean.FALSE; } return null; } }); } /* * Legacy mechanism to load resource bundles */ ! private static ResourceBundle loadBundle(LoadSession loadSession, List<String> formats, Control control, boolean reload) { // Here we actually load the bundle in the order of formats // specified by the getFormats() value. ! Locale targetLocale = loadSession.getLocale(); ResourceBundle bundle = null; for (String format : formats) { try { // ResourceBundle.Control.newBundle may be overridden ! bundle = control.newBundle(loadSession.getName(), targetLocale, format, ! loadSession.getLoader(), reload); } catch (LinkageError | Exception error) { // We need to handle the LinkageError case due to // inconsistent case-sensitivity in ClassLoader. // See 6572242 for details. ! loadSession.setCause(error); } if (bundle != null) { // Set the format in the cache key so that it can be // used when calling needsReload later. ! bundle.format = format; ! bundle.name = loadSession.getName(); bundle.locale = targetLocale; // Bundle provider might reuse instances. So we should make // sure to clear the expired flag here. bundle.expired = false; break;
*** 1971,1981 **** return bundle; } private static boolean isValidBundle(ResourceBundle bundle) { ! return bundle != null && bundle != NONEXISTENT_BUNDLE; } /** * Determines whether any of resource bundles in the parent chain, * including the leaf, have expired. --- 1823,1833 ---- return bundle; } private static boolean isValidBundle(ResourceBundle bundle) { ! return bundle != null && !(bundle instanceof NONEXISTENT_BUNDLE); } /** * Determines whether any of resource bundles in the parent chain, * including the leaf, have expired.
*** 1984,2000 **** long now = System.currentTimeMillis(); while (bundle != null) { if (bundle.expired) { return false; } ! CacheKey key = bundle.cacheKey; ! if (key != null) { ! long expirationTime = key.expirationTime; if (expirationTime >= 0 && expirationTime <= now) { return false; } - } bundle = bundle.parent; } return true; } --- 1836,1849 ---- long now = System.currentTimeMillis(); while (bundle != null) { if (bundle.expired) { return false; } ! long expirationTime = bundle.expirationTime; if (expirationTime >= 0 && expirationTime <= now) { return false; } bundle = bundle.parent; } return true; }
*** 2018,2045 **** /** * Finds a bundle in the cache. Any expired bundles are marked as * `expired' and removed from the cache upon return. * ! * @param cacheKey the key to look up the cache * @param control the Control to be used for the expiration control * @return the cached bundle, or null if the bundle is not found in the * cache or its parent has expired. <code>bundle.expire</code> is true * upon return if the bundle in the cache has expired. */ ! private static ResourceBundle findBundleInCache(CacheKey cacheKey, Control control) { ! BundleReference bundleRef = cacheList.get(cacheKey); if (bundleRef == null) { return null; } ResourceBundle bundle = bundleRef.get(); if (bundle == null) { return null; } ResourceBundle p = bundle.parent; ! assert p != NONEXISTENT_BUNDLE; // If the parent has expired, then this one must also expire. We // check only the immediate parent because the actual loading is // done from the root (base) to leaf (child) and the purpose of // checking is to propagate expiration towards the leaf. For // example, if the requested locale is ja_JP_JP and there are --- 1867,1894 ---- /** * Finds a bundle in the cache. Any expired bundles are marked as * `expired' and removed from the cache upon return. * ! * @param loadSession the load session used look up the cache * @param control the Control to be used for the expiration control * @return the cached bundle, or null if the bundle is not found in the * cache or its parent has expired. <code>bundle.expire</code> is true * upon return if the bundle in the cache has expired. */ ! private static ResourceBundle findBundleInCache(LoadSession loadSession, Control control) { ! BundleReference bundleRef = loadSession.getFromCache(); if (bundleRef == null) { return null; } ResourceBundle bundle = bundleRef.get(); if (bundle == null) { return null; } ResourceBundle p = bundle.parent; ! assert !(p instanceof NONEXISTENT_BUNDLE); // If the parent has expired, then this one must also expire. We // check only the immediate parent because the actual loading is // done from the root (base) to leaf (child) and the purpose of // checking is to propagate expiration towards the leaf. For // example, if the requested locale is ja_JP_JP and there are
*** 2070,2184 **** // We could check the entire parent chain to see if there's any in // the chain that has expired. But this process may never end. An // extreme case would be that getTimeToLive returns 0 and // needsReload always returns true. if (p != null && p.expired) { ! assert bundle != NONEXISTENT_BUNDLE; bundle.expired = true; ! bundle.cacheKey = null; ! cacheList.remove(cacheKey, bundleRef); bundle = null; } else { ! CacheKey key = bundleRef.getCacheKey(); ! long expirationTime = key.expirationTime; if (!bundle.expired && expirationTime >= 0 && expirationTime <= System.currentTimeMillis()) { // its TTL period has expired. ! if (bundle != NONEXISTENT_BUNDLE) { // Synchronize here to call needsReload to avoid // redundant concurrent calls for the same bundle. synchronized (bundle) { ! expirationTime = key.expirationTime; if (!bundle.expired && expirationTime >= 0 && expirationTime <= System.currentTimeMillis()) { try { ! bundle.expired = control.needsReload(key.getName(), ! key.getLocale(), ! key.getFormat(), ! key.getLoader(), bundle, ! key.loadTime); } catch (Exception e) { ! cacheKey.setCause(e); } if (bundle.expired) { // If the bundle needs to be reloaded, then // remove the bundle from the cache, but // return the bundle with the expired flag // on. ! bundle.cacheKey = null; ! cacheList.remove(cacheKey, bundleRef); } else { // Update the expiration control info. and reuse // the same bundle instance ! setExpirationTime(key, control); } } } } else { // We just remove NONEXISTENT_BUNDLE from the cache. ! cacheList.remove(cacheKey, bundleRef); bundle = null; } } } return bundle; } /** * Put a new bundle in the cache. * ! * @param cacheKey the key for the resource bundle * @param bundle the resource bundle to be put in the cache * @return the ResourceBundle for the cacheKey; if someone has put * the bundle before this call, the one found in the cache is * returned. */ ! private static ResourceBundle putBundleInCache(CacheKey cacheKey, ResourceBundle bundle, Control control) { ! setExpirationTime(cacheKey, control); ! if (cacheKey.expirationTime != Control.TTL_DONT_CACHE) { ! CacheKey key = (CacheKey) cacheKey.clone(); ! BundleReference bundleRef = new BundleReference(bundle, referenceQueue, key); ! bundle.cacheKey = key; // Put the bundle in the cache if it's not been in the cache. ! BundleReference result = cacheList.putIfAbsent(key, bundleRef); // If someone else has put the same bundle in the cache before // us and it has not expired, we should use the one in the cache. ! if (result != null) { ! ResourceBundle rb = result.get(); if (rb != null && !rb.expired) { ! // Clear the back link to the cache key ! bundle.cacheKey = null; ! bundle = rb; ! // Clear the reference in the BundleReference so that ! // it won't be enqueued. ! bundleRef.clear(); } else { ! // Replace the invalid (garbage collected or expired) // instance with the valid one. ! cacheList.put(key, bundleRef); } } } return bundle; } ! private static void setExpirationTime(CacheKey cacheKey, Control control) { ! long ttl = control.getTimeToLive(cacheKey.getName(), ! cacheKey.getLocale()); if (ttl >= 0) { // If any expiration time is specified, set the time to be // expired in the cache. long now = System.currentTimeMillis(); ! cacheKey.loadTime = now; ! cacheKey.expirationTime = now + ttl; } else if (ttl >= Control.TTL_NO_EXPIRATION_CONTROL) { ! cacheKey.expirationTime = ttl; } else { throw new IllegalArgumentException("Invalid Control: TTL=" + ttl); } } --- 1919,2035 ---- // We could check the entire parent chain to see if there's any in // the chain that has expired. But this process may never end. An // extreme case would be that getTimeToLive returns 0 and // needsReload always returns true. if (p != null && p.expired) { ! assert !(bundle instanceof NONEXISTENT_BUNDLE); bundle.expired = true; ! bundleRef.remove(); bundle = null; } else { ! long expirationTime = bundle.expirationTime; if (!bundle.expired && expirationTime >= 0 && expirationTime <= System.currentTimeMillis()) { // its TTL period has expired. ! if (!(bundle instanceof NONEXISTENT_BUNDLE)) { // Synchronize here to call needsReload to avoid // redundant concurrent calls for the same bundle. synchronized (bundle) { ! expirationTime = bundle.expirationTime; if (!bundle.expired && expirationTime >= 0 && expirationTime <= System.currentTimeMillis()) { try { ! bundle.expired = control.needsReload(bundle.getBaseBundleName(), ! bundle.getLocale(), ! bundle.format, ! loadSession.getLoader(), bundle, ! bundle.loadTime); } catch (Exception e) { ! loadSession.setCause(e); } if (bundle.expired) { // If the bundle needs to be reloaded, then // remove the bundle from the cache, but // return the bundle with the expired flag // on. ! bundleRef.remove(); } else { // Update the expiration control info. and reuse // the same bundle instance ! setExpirationTime(bundle, loadSession, control); } } } } else { // We just remove NONEXISTENT_BUNDLE from the cache. ! bundleRef.remove(); bundle = null; } } } return bundle; } /** * Put a new bundle in the cache. * ! * @param loadSession the key for the resource bundle * @param bundle the resource bundle to be put in the cache * @return the ResourceBundle for the cacheKey; if someone has put * the bundle before this call, the one found in the cache is * returned. */ ! private static ResourceBundle putBundleInCache(LoadSession loadSession, ResourceBundle bundle, Control control) { ! setExpirationTime(bundle, loadSession, control); ! if (bundle.expirationTime != Control.TTL_DONT_CACHE) { ! ClassLoaderValue<BundleReference>.Sub<Module>.Sub<String>.Sub<Locale> key ! = loadSession.key(); ! ClassLoader loader = loadSession.getLoader(); ! BundleReference bundleRef = new BundleReference( ! bundle, referenceQueue, loader, key); // Put the bundle in the cache if it's not been in the cache. ! BundleReference oldBundleRef = key.putIfAbsent(loader, bundleRef); // If someone else has put the same bundle in the cache before // us and it has not expired, we should use the one in the cache. ! while (oldBundleRef != null) { ! ResourceBundle rb = oldBundleRef.get(); if (rb != null && !rb.expired) { ! return rb; } else { ! // Try to replace the invalid (garbage collected or expired) // instance with the valid one. ! if (key.replace(loader, oldBundleRef, bundleRef)) { ! break; ! } else { ! // Someone else must have already replaced it or it was ! // removed. Retry putting the bundle in the cache. ! oldBundleRef = key.putIfAbsent(loader, bundleRef); ! } } } } return bundle; } ! private static void setExpirationTime(ResourceBundle bundle, ! LoadSession loadSession, ! Control control) { ! long ttl = control.getTimeToLive(loadSession.getName(), ! loadSession.getLocale()); if (ttl >= 0) { // If any expiration time is specified, set the time to be // expired in the cache. long now = System.currentTimeMillis(); ! bundle.loadTime = now; ! bundle.expirationTime = now + ttl; } else if (ttl >= Control.TTL_NO_EXPIRATION_CONTROL) { ! bundle.expirationTime = ttl; } else { throw new IllegalArgumentException("Invalid Control: TTL=" + ttl); } }
*** 2227,2239 **** public static final void clearCache(Module module) { clearCache(module.getClassLoader(), module); } 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); } /** * Gets an object for the given key from this resource bundle. * Returns null if this resource bundle does not contain an --- 2078,2088 ---- public static final void clearCache(Module module) { clearCache(module.getClassLoader(), module); } private static void clearCache(ClassLoader loader, Module module) { ! cache.sub(module).removeAll(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 >