< prev index next >

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

Print this page




  27  * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
  28  * (C) Copyright IBM Corp. 1996 - 1999 - All Rights Reserved
  29  *
  30  * The original version of this source code and documentation
  31  * is copyrighted and owned by Taligent, Inc., a wholly-owned
  32  * subsidiary of IBM. These materials are provided under terms
  33  * of a License Agreement between Taligent and Sun. This technology
  34  * is protected by multiple US and International patents.
  35  *
  36  * This notice and attribution to Taligent may not be removed.
  37  * Taligent is a registered trademark of Taligent, Inc.
  38  *
  39  */
  40 
  41 package java.util;
  42 
  43 import java.io.IOException;
  44 import java.io.InputStream;
  45 import java.lang.ref.ReferenceQueue;
  46 import java.lang.ref.SoftReference;
  47 import java.lang.ref.WeakReference;
  48 import java.lang.reflect.Constructor;
  49 import java.lang.reflect.InvocationTargetException;
  50 import java.lang.reflect.Modifier;
  51 import java.lang.reflect.Module;
  52 import java.net.JarURLConnection;
  53 import java.net.URL;
  54 import java.net.URLConnection;
  55 import java.security.AccessController;
  56 import java.security.PrivilegedAction;
  57 import java.security.PrivilegedActionException;
  58 import java.security.PrivilegedExceptionAction;
  59 import java.util.concurrent.ConcurrentHashMap;
  60 import java.util.concurrent.ConcurrentMap;
  61 import java.util.jar.JarEntry;
  62 import java.util.spi.ResourceBundleControlProvider;
  63 import java.util.spi.ResourceBundleProvider;
  64 
  65 import jdk.internal.misc.JavaUtilResourceBundleAccess;
  66 import jdk.internal.misc.SharedSecrets;
  67 import jdk.internal.reflect.CallerSensitive;
  68 import jdk.internal.reflect.Reflection;


  69 import sun.util.locale.BaseLocale;
  70 import sun.util.locale.LocaleObjectCache;
  71 import sun.util.locale.provider.ResourceBundleProviderSupport;
  72 import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
  73 
  74 
  75 /**
  76  *
  77  * Resource bundles contain locale-specific objects.  When your program needs a
  78  * locale-specific resource, a <code>String</code> for example, your program can
  79  * load it from the resource bundle that is appropriate for the current user's
  80  * locale. In this way, you can write program code that is largely independent
  81  * of the user's locale isolating most, if not all, of the locale-specific
  82  * information in resource bundles.
  83  *
  84  * <p>
  85  * This allows you to write programs that can:
  86  * <UL>
  87  * <LI> be easily localized, or translated, into different languages
  88  * <LI> handle multiple locales at once


 329  *         return new HashSet&lt;String&gt;(Arrays.asList("cancelKey"));
 330  *     }
 331  * }
 332  * </pre>
 333  * </blockquote>
 334  * You do not have to restrict yourself to using a single family of
 335  * <code>ResourceBundle</code>s. For example, you could have a set of bundles for
 336  * exception messages, <code>ExceptionResources</code>
 337  * (<code>ExceptionResources_fr</code>, <code>ExceptionResources_de</code>, ...),
 338  * and one for widgets, <code>WidgetResource</code> (<code>WidgetResources_fr</code>,
 339  * <code>WidgetResources_de</code>, ...); breaking up the resources however you like.
 340  *
 341  * @see ListResourceBundle
 342  * @see PropertyResourceBundle
 343  * @see MissingResourceException
 344  * @see ResourceBundleProvider
 345  * @since 1.1
 346  */
 347 public abstract class ResourceBundle {
 348 
 349     /** initial size of the bundle cache */
 350     private static final int INITIAL_CACHE_SIZE = 32;
 351 
 352     static {
 353         SharedSecrets.setJavaUtilResourceBundleAccess(
 354             new JavaUtilResourceBundleAccess() {
 355                 @Override
 356                 public void setParent(ResourceBundle bundle,
 357                                       ResourceBundle parent) {
 358                     bundle.setParent(parent);
 359                 }
 360 
 361                 @Override
 362                 public ResourceBundle getParent(ResourceBundle bundle) {
 363                     return bundle.parent;
 364                 }
 365 
 366                 @Override
 367                 public void setLocale(ResourceBundle bundle, Locale locale) {
 368                     bundle.locale = locale;
 369                 }
 370 
 371                 @Override
 372                 public void setName(ResourceBundle bundle, String name) {
 373                     bundle.name = name;
 374                 }
 375             });
 376     }
 377 
 378     /** constant indicating that no resource bundle exists */
 379     private static final ResourceBundle NONEXISTENT_BUNDLE = new ResourceBundle() {


 380             public Enumeration<String> getKeys() { return null; }
 381             protected Object handleGetObject(String key) { return null; }
 382             public String toString() { return "NONEXISTENT_BUNDLE"; }
 383         };
 384 
 385 
 386     /**
 387      * The cache is a map from cache keys (with bundle base name, locale, and
 388      * class loader) to either a resource bundle or NONEXISTENT_BUNDLE wrapped by a
 389      * BundleReference.
 390      *
 391      * The cache is a ConcurrentMap, allowing the cache to be searched
 392      * concurrently by multiple threads.  This will also allow the cache keys
 393      * to be reclaimed along with the ClassLoaders they reference.
 394      *
 395      * This variable would be better named "cache", but we keep the old
 396      * name for compatibility with some workarounds for bug 4212439.
 397      */
 398     private static final ConcurrentMap<CacheKey, BundleReference> cacheList
 399         = new ConcurrentHashMap<>(INITIAL_CACHE_SIZE);
 400 
 401     /**
 402      * Queue for reference objects referring to class loaders or bundles.
 403      */
 404     private static final ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
 405 
 406     /**
 407      * Returns the base name of this bundle, if known, or {@code null} if unknown.
 408      *
 409      * If not null, then this is the value of the {@code baseName} parameter
 410      * that was passed to the {@code ResourceBundle.getBundle(...)} method
 411      * when the resource bundle was loaded.
 412      *
 413      * @return The base name of the resource bundle, as provided to and expected
 414      * by the {@code ResourceBundle.getBundle(...)} methods.
 415      *
 416      * @see #getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader)
 417      *
 418      * @since 1.8
 419      */
 420     public String getBaseBundleName() {
 421         return name;
 422     }
 423 
 424     /**
 425      * The parent bundle of this bundle.
 426      * The parent bundle is searched by {@link #getObject getObject}
 427      * when this bundle does not contain a particular resource.
 428      */
 429     protected ResourceBundle parent = null;
 430 
 431     /**
 432      * The locale for this bundle.
 433      */
 434     private Locale locale = null;
 435 
 436     /**
 437      * The base bundle name for this bundle.
 438      */
 439     private String name;
 440 
 441     /**






 442      * The flag indicating this bundle has expired in the cache.
 443      */
 444     private volatile boolean expired;
 445 
 446     /**
 447      * The back link to the cache key. null if this bundle isn't in
 448      * the cache (yet) or has expired.
 449      */
 450     private volatile CacheKey cacheKey;






 451 
 452     /**
 453      * A Set of the keys contained only in this ResourceBundle.
 454      */
 455     private volatile Set<String> keySet;
 456 
 457     private static final List<ResourceBundleControlProvider> providers;
 458 
 459     static {
 460         List<ResourceBundleControlProvider> list = null;
 461         ServiceLoader<ResourceBundleControlProvider> serviceLoaders
 462                 = ServiceLoader.loadInstalled(ResourceBundleControlProvider.class);
 463         for (ResourceBundleControlProvider provider : serviceLoaders) {
 464             if (list == null) {
 465                 list = new ArrayList<>();
 466             }
 467             list.add(provider);
 468         }
 469         providers = list;
 470     }


 601             }
 602             return ClassLoader.getSystemResource(name);
 603         }
 604         public InputStream getResourceAsStream(String name) {
 605             ClassLoader loader = ClassLoader.getSystemClassLoader();
 606             if (loader != null) {
 607                 return loader.getResourceAsStream(name);
 608             }
 609             return ClassLoader.getSystemResourceAsStream(name);
 610         }
 611     }
 612 
 613     /**
 614      * Sets the parent bundle of this bundle.
 615      * The parent bundle is searched by {@link #getObject getObject}
 616      * when this bundle does not contain a particular resource.
 617      *
 618      * @param parent this bundle's parent bundle.
 619      */
 620     protected void setParent(ResourceBundle parent) {
 621         assert parent != NONEXISTENT_BUNDLE;
 622         this.parent = parent;
 623     }
 624 
 625     /**
 626      * Key used for cached resource bundles.  The key checks the base
 627      * name, the locale, the class loader, and the caller module
 628      * to determine if the resource is a match to the requested one.
 629      * The loader may be null, but the base name, the locale and
 630      * module must have a non-null value.
 631      */
 632     private static class CacheKey implements Cloneable {
 633         // These four are the actual keys for lookup in Map.

 634         private String name;
 635         private Locale locale;
 636         private KeyElementReference<ClassLoader> loaderRef;
 637         private KeyElementReference<Module> moduleRef;
 638 
 639 
 640         // bundle format which is necessary for calling
 641         // Control.needsReload().
 642         private String format;
 643 
 644         // These time values are in CacheKey so that NONEXISTENT_BUNDLE
 645         // doesn't need to be cloned for caching.
 646 
 647         // The time when the bundle has been loaded
 648         private volatile long loadTime;


 649 
 650         // The time when the bundle expires in the cache, or either
 651         // Control.TTL_DONT_CACHE or Control.TTL_NO_EXPIRATION_CONTROL.
 652         private volatile long expirationTime;

 653 
 654         // Placeholder for an error report by a Throwable
 655         private Throwable cause;
 656 
 657         // Hash code value cache to avoid recalculating the hash code
 658         // of this instance.
 659         private int hashCodeCache;
 660 
 661         // ResourceBundleProviders for loading ResourceBundles
 662         private ServiceLoader<ResourceBundleProvider> providers;
 663         private boolean providersChecked;
 664 
 665         // Boolean.TRUE if the factory method caller provides a ResourceBundleProvier.
 666         private Boolean callerHasProvider;
 667 
 668         CacheKey(String baseName, Locale locale, ClassLoader loader, Module module) {
 669             Objects.requireNonNull(module);
 670 
 671             this.name = baseName;
 672             this.locale = locale;
 673             if (loader == null) {
 674                 this.loaderRef = null;
 675             } else {
 676                 this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this);
 677             }
 678             this.moduleRef = new KeyElementReference<>(module, referenceQueue, this);
 679             calculateHashCode();
 680         }
 681 
 682         String getName() {
 683             return name;
 684         }
 685 
 686         CacheKey setName(String baseName) {
 687             if (!this.name.equals(baseName)) {
 688                 this.name = baseName;
 689                 calculateHashCode();
 690             }
 691             return this;
 692         }
 693 
 694         Locale getLocale() {
 695             return locale;
 696         }
 697 
 698         CacheKey setLocale(Locale locale) {
 699             if (!this.locale.equals(locale)) {
 700                 this.locale = locale;
 701                 calculateHashCode();
 702             }
 703             return this;
 704         }
 705 
 706         ClassLoader getLoader() {
 707             return (loaderRef != null) ? loaderRef.get() : null;
 708         }
 709 
 710         Module getModule() {
 711             return moduleRef.get();
 712         }
 713 
 714         ServiceLoader<ResourceBundleProvider> getProviders() {
 715             if (!providersChecked) {
 716                 providers = getServiceLoader(getModule(), name);
 717                 providersChecked = true;
 718             }
 719             return providers;
 720         }
 721 
 722         boolean hasProviders() {
 723             return getProviders() != null;
 724         }
 725 
 726         boolean callerHasProvider() {
 727             return callerHasProvider == Boolean.TRUE;
 728         }
 729 
 730         @Override
 731         public boolean equals(Object other) {
 732             if (this == other) {
 733                 return true;
 734             }
 735             try {
 736                 final CacheKey otherEntry = (CacheKey)other;
 737                 //quick check to see if they are not equal
 738                 if (hashCodeCache != otherEntry.hashCodeCache) {
 739                     return false;
 740                 }
 741                 //are the names the same?
 742                 if (!name.equals(otherEntry.name)) {
 743                     return false;
 744                 }
 745                 // are the locales the same?
 746                 if (!locale.equals(otherEntry.locale)) {
 747                     return false;
 748                 }
 749                 //are refs (both non-null) or (both null)?
 750                 if (loaderRef == null) {
 751                     return otherEntry.loaderRef == null;
 752                 }
 753                 ClassLoader loader = getLoader();
 754                 Module module = getModule();
 755                 return (otherEntry.loaderRef != null)
 756                         // with a null reference we can no longer find
 757                         // out which class loader or module was referenced; so
 758                         // treat it as unequal
 759                         && (loader != null)
 760                         && (loader == otherEntry.getLoader())
 761                         && (module != null)
 762                         && (module.equals(otherEntry.getModule()));
 763             } catch (NullPointerException | ClassCastException e) {
 764             }
 765             return false;
 766         }
 767 
 768         @Override
 769         public int hashCode() {
 770             return hashCodeCache;
 771         }
 772 
 773         private void calculateHashCode() {
 774             hashCodeCache = name.hashCode() << 3;
 775             hashCodeCache ^= locale.hashCode();
 776             ClassLoader loader = getLoader();
 777             if (loader != null) {
 778                 hashCodeCache ^= loader.hashCode();
 779             }
 780             Module module = getModule();
 781             if (module != null) {
 782                 hashCodeCache ^= module.hashCode();
 783             }
 784         }
 785 
 786         @Override
 787         public Object clone() {
 788             try {
 789                 CacheKey clone = (CacheKey) super.clone();
 790                 if (loaderRef != null) {
 791                     clone.loaderRef = new KeyElementReference<>(getLoader(),
 792                                                                 referenceQueue, clone);
 793                 }
 794                 clone.moduleRef = new KeyElementReference<>(getModule(),
 795                                                             referenceQueue, clone);
 796                 // Clear the reference to ResourceBundleProviders and the flag
 797                 clone.providers = null;
 798                 clone.providersChecked = false;
 799                 // Clear the reference to a Throwable
 800                 clone.cause = null;
 801                 // Clear callerHasProvider
 802                 clone.callerHasProvider = null;
 803                 return clone;
 804             } catch (CloneNotSupportedException e) {
 805                 //this should never happen
 806                 throw new InternalError(e);
 807             }
 808         }
 809 
 810         String getFormat() {
 811             return format;
 812         }
 813 
 814         void setFormat(String format) {
 815             this.format = format;
 816         }
 817 
 818         private void setCause(Throwable cause) {
 819             if (this.cause == null) {
 820                 this.cause = cause;
 821             } else {
 822                 // Override the cause if the previous one is
 823                 // ClassNotFoundException.
 824                 if (this.cause instanceof ClassNotFoundException) {
 825                     this.cause = cause;
 826                 }
 827             }
 828         }
 829 
 830         private Throwable getCause() {
 831             return cause;
 832         }
 833 
 834         @Override
 835         public String toString() {
 836             String l = locale.toString();
 837             if (l.length() == 0) {
 838                 if (locale.getVariant().length() != 0) {
 839                     l = "__" + locale.getVariant();
 840                 } else {
 841                     l = "\"\"";
 842                 }
 843             }
 844             return "CacheKey[" + name + ", lc=" + l + ", ldr=" + getLoader()
 845                 + "(format=" + format + ")]";
 846         }
 847     }
 848 
 849     /**
 850      * The common interface to get a CacheKey in LoaderReference and
 851      * BundleReference.
 852      */
 853     private static interface CacheKeyReference {
 854         public CacheKey getCacheKey();
 855     }
 856 
 857     /**
 858      * References to a CacheKey element as a WeakReference so that it can be
 859      * garbage collected when nobody else is using it.
 860      */
 861     private static class KeyElementReference<T> extends WeakReference<T>
 862                                                 implements CacheKeyReference {
 863         private final CacheKey cacheKey;
 864 
 865         KeyElementReference(T referent, ReferenceQueue<Object> q, CacheKey key) {


 866             super(referent, q);
 867             cacheKey = key;

 868         }
 869 
 870         @Override
 871         public CacheKey getCacheKey() {
 872             return cacheKey;
 873         }
 874     }
 875 
 876     /**
 877      * References to bundles are soft references so that they can be garbage
 878      * collected when they have no hard references.
 879      */
 880     private static class BundleReference extends SoftReference<ResourceBundle>
 881                                          implements CacheKeyReference {
 882         private final CacheKey cacheKey;
 883 
 884         BundleReference(ResourceBundle referent, ReferenceQueue<Object> q, CacheKey key) {
 885             super(referent, q);
 886             cacheKey = key;
 887         }
 888 
 889         @Override
 890         public CacheKey getCacheKey() {
 891             return cacheKey;
 892         }
 893     }
 894 
 895     /**
 896      * Gets a resource bundle using the specified base name, the default locale,
 897      * and the caller's class loader. Calling this method is equivalent to calling
 898      * <blockquote>
 899      * <code>getBundle(baseName, Locale.getDefault(), this.getClass().getClassLoader())</code>,
 900      * </blockquote>
 901      * except that <code>getClassLoader()</code> is run with the security
 902      * privileges of <code>ResourceBundle</code>.
 903      * See {@link #getBundle(String, Locale, ClassLoader) getBundle}
 904      * for a complete description of the search and instantiation strategy.
 905      *
 906      * @param baseName the base name of the resource bundle, a fully qualified class name
 907      * @exception java.lang.NullPointerException
 908      *     if <code>baseName</code> is <code>null</code>
 909      * @exception MissingResourceException
 910      *     if no resource bundle for the specified base name can be found
 911      * @return a resource bundle for the given base name and the default locale


1603                                                       Control control) {
1604         Objects.requireNonNull(module);
1605         if (caller.getModule() != module) {
1606             SecurityManager sm = System.getSecurityManager();
1607             if (sm != null) {
1608                 sm.checkPermission(GET_CLASSLOADER_PERMISSION);
1609             }
1610         }
1611         return getBundleImpl(baseName, locale, getLoader(module), module, control);
1612     }
1613 
1614     private static ResourceBundle getBundleImpl(String baseName,
1615                                                 Locale locale,
1616                                                 ClassLoader loader,
1617                                                 Module module,
1618                                                 Control control) {
1619         if (locale == null || control == null) {
1620             throw new NullPointerException();
1621         }
1622 
1623         // We create a CacheKey here for use by this call. The base name
1624         // loader, and module will never change during the bundle loading
1625         // process. We have to make sure that the locale is set before
1626         // using it as a cache key.
1627         CacheKey cacheKey = new CacheKey(baseName, locale, loader, module);
1628         ResourceBundle bundle = null;
1629 
1630         // Quick lookup of the cache.
1631         BundleReference bundleRef = cacheList.get(cacheKey);

1632         if (bundleRef != null) {
1633             bundle = bundleRef.get();
1634             bundleRef = null;
1635         }
1636 
1637         // If this bundle and all of its parents are valid (not expired),
1638         // then return this bundle. If any of the bundles is expired, we
1639         // don't call control.needsReload here but instead drop into the
1640         // complete loading process below.
1641         if (isValidBundle(bundle) && hasValidParentChain(bundle)) {
1642             return bundle;
1643         }
1644 
1645         // No valid bundle was found in the cache, so we need to load the
1646         // resource bundle and its parents.
1647 
1648         boolean isKnownControl = (control == Control.INSTANCE) ||
1649                                    (control instanceof SingleFormatControl);
1650         List<String> formats = control.getFormats(baseName);
1651         if (!isKnownControl && !checkList(formats)) {
1652             throw new IllegalArgumentException("Invalid Control: getFormats");
1653         }
1654 
1655         ResourceBundle baseBundle = null;
1656         for (Locale targetLocale = locale;
1657              targetLocale != null;
1658              targetLocale = control.getFallbackLocale(baseName, targetLocale)) {
1659             List<Locale> candidateLocales = control.getCandidateLocales(baseName, targetLocale);
1660             if (!isKnownControl && !checkList(candidateLocales)) {
1661                 throw new IllegalArgumentException("Invalid Control: getCandidateLocales");
1662             }
1663 
1664             bundle = findBundle(cacheKey, module, candidateLocales, formats, 0, control, baseBundle);
1665 
1666             // If the loaded bundle is the base bundle and exactly for the
1667             // requested locale or the only candidate locale, then take the
1668             // bundle as the resulting one. If the loaded bundle is the base
1669             // bundle, it's put on hold until we finish processing all
1670             // fallback locales.
1671             if (isValidBundle(bundle)) {
1672                 boolean isBaseBundle = Locale.ROOT.equals(bundle.locale);
1673                 if (!isBaseBundle || bundle.locale.equals(locale)
1674                     || (candidateLocales.size() == 1
1675                         && bundle.locale.equals(candidateLocales.get(0)))) {
1676                     break;
1677                 }
1678 
1679                 // If the base bundle has been loaded, keep the reference in
1680                 // baseBundle so that we can avoid any redundant loading in case
1681                 // the control specify not to cache bundles.
1682                 if (isBaseBundle && baseBundle == null) {
1683                     baseBundle = bundle;
1684                 }
1685             }
1686         }
1687 
1688         if (bundle == null) {
1689             if (baseBundle == null) {
1690                 throwMissingResourceException(baseName, locale, cacheKey.getCause());
1691             }
1692             bundle = baseBundle;
1693         }
1694 
1695         return bundle;
1696     }
1697 
1698     /**
1699      * Checks if the given <code>List</code> is not null, not empty,
1700      * not having null in its elements.
1701      */
1702     private static boolean checkList(List<?> a) {
1703         boolean valid = (a != null && !a.isEmpty());
1704         if (valid) {
1705             int size = a.size();
1706             for (int i = 0; valid && i < size; i++) {
1707                 valid = (a.get(i) != null);
1708             }
1709         }
1710         return valid;
1711     }
1712 
1713     private static ResourceBundle findBundle(CacheKey cacheKey,
1714                                              Module module,
1715                                              List<Locale> candidateLocales,
1716                                              List<String> formats,
1717                                              int index,
1718                                              Control control,
1719                                              ResourceBundle baseBundle) {
1720         Locale targetLocale = candidateLocales.get(index);
1721         ResourceBundle parent = null;
1722         if (index != candidateLocales.size() - 1) {
1723             parent = findBundle(cacheKey, module, candidateLocales, formats, index + 1,
1724                                 control, baseBundle);
1725         } else if (baseBundle != null && Locale.ROOT.equals(targetLocale)) {
1726             return baseBundle;
1727         }
1728 
1729         // Before we do the real loading work, see whether we need to
1730         // do some housekeeping: If references to class loaders or
1731         // resource bundles have been nulled out, remove all related
1732         // information from the cache.
1733         Object ref;
1734         while ((ref = referenceQueue.poll()) != null) {
1735             cacheList.remove(((CacheKeyReference)ref).getCacheKey());
1736         }
1737 
1738         // flag indicating the resource bundle has expired in the cache
1739         boolean expiredBundle = false;
1740 
1741         // First, look up the cache to see if it's in the cache, without
1742         // attempting to load bundle.
1743         cacheKey.setLocale(targetLocale);
1744         ResourceBundle bundle = findBundleInCache(cacheKey, control);
1745         if (isValidBundle(bundle)) {
1746             expiredBundle = bundle.expired;
1747             if (!expiredBundle) {
1748                 // If its parent is the one asked for by the candidate
1749                 // locales (the runtime lookup path), we can take the cached
1750                 // one. (If it's not identical, then we'd have to check the
1751                 // parent's parents to be consistent with what's been
1752                 // requested.)
1753                 if (bundle.parent == parent) {
1754                     return bundle;
1755                 }
1756                 // Otherwise, remove the cached one since we can't keep
1757                 // the same bundles having different parents.
1758                 BundleReference bundleRef = cacheList.get(cacheKey);
1759                 if (bundleRef != null && bundleRef.get() == bundle) {
1760                     cacheList.remove(cacheKey, bundleRef);
1761                 }
1762             }
1763         }
1764 
1765         if (bundle != NONEXISTENT_BUNDLE) {
1766             CacheKey constKey = (CacheKey) cacheKey.clone();
1767 
1768             try {
1769                 if (module.isNamed()) {
1770                     bundle = loadBundle(cacheKey, formats, control, module);
1771                 } else {
1772                     bundle = loadBundle(cacheKey, formats, control, expiredBundle);
1773                 }
1774                 if (bundle != null) {
1775                     if (bundle.parent == null) {
1776                         bundle.setParent(parent);
1777                     }
1778                     bundle.locale = targetLocale;
1779                     bundle = putBundleInCache(cacheKey, bundle, control);
1780                     return bundle;
1781                 }
1782 
1783                 // Put NONEXISTENT_BUNDLE in the cache as a mark that there's no bundle
1784                 // instance for the locale.
1785                 putBundleInCache(cacheKey, NONEXISTENT_BUNDLE, control);
1786             } finally {
1787                 if (constKey.getCause() instanceof InterruptedException) {
1788                     Thread.currentThread().interrupt();
1789                 }
1790             }
1791         }
1792         return parent;
1793     }
1794 
1795     private static final String UNKNOWN_FORMAT = "";
1796 
1797     /*
1798      * Loads a ResourceBundle in named modules
1799      */
1800     private static ResourceBundle loadBundle(CacheKey cacheKey,
1801                                              List<String> formats,
1802                                              Control control,
1803                                              Module module) {
1804         String baseName = cacheKey.getName();
1805         Locale targetLocale = cacheKey.getLocale();
1806 
1807         ResourceBundle bundle = null;
1808         if (cacheKey.hasProviders()) {

1809             bundle = loadBundleFromProviders(baseName, targetLocale,
1810                                              cacheKey.getProviders(), cacheKey);
1811             if (bundle != null) {
1812                 cacheKey.setFormat(UNKNOWN_FORMAT);
1813             }
1814         }
1815         // If none of providers returned a bundle and the caller has no provider,
1816         // look up module-local bundles.
1817         if (bundle == null && !cacheKey.callerHasProvider()) {
1818             String bundleName = control.toBundleName(baseName, targetLocale);
1819             for (String format : formats) {
1820                 try {
1821                     switch (format) {
1822                     case "java.class":
1823                         PrivilegedAction<ResourceBundle> pa = ()
1824                                 -> ResourceBundleProviderSupport
1825                                     .loadResourceBundle(module, bundleName);
1826                         bundle = AccessController.doPrivileged(pa, null, GET_CLASSLOADER_PERMISSION);
1827                         break;
1828                     case "java.properties":
1829                         bundle = ResourceBundleProviderSupport.loadPropertyResourceBundle(module, bundleName);
1830                         break;
1831                     default:
1832                         throw new InternalError("unexpected format: " + format);
1833                     }
1834 
1835                     if (bundle != null) {
1836                         cacheKey.setFormat(format);
1837                         break;
1838                     }
1839                 } catch (Exception e) {
1840                     cacheKey.setCause(e);
1841                 }
1842             }
1843         }
1844         return bundle;
1845     }
1846 
1847     private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module,






1848                                                                           String baseName) {
1849         if (!module.isNamed()) {
1850             return null;
1851         }
1852         PrivilegedAction<ClassLoader> pa = module::getClassLoader;
1853         ClassLoader loader = AccessController.doPrivileged(pa);
1854         return getServiceLoader(module, loader, baseName);
1855     }
1856 
1857         /**
1858          * Returns a ServiceLoader that will find providers that are bound to
1859          * a given module that may be named or unnamed.

1860          */
1861     private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module,
1862                                                                           ClassLoader loader,
1863                                                                           String baseName)
1864     {
1865         // Look up <baseName> + "Provider"
1866         String providerName = baseName + "Provider";
1867         // Use the class loader of the getBundle caller so that the caller's
1868         // visibility of the provider type is checked.
1869         Class<ResourceBundleProvider> service = AccessController.doPrivileged(
1870             new PrivilegedAction<>() {
1871                 @Override
1872                 public Class<ResourceBundleProvider> run() {
1873                     try {
1874                         Class<?> c = Class.forName(providerName, false, loader);
1875                         if (ResourceBundleProvider.class.isAssignableFrom(c)) {
1876                             @SuppressWarnings("unchecked")
1877                             Class<ResourceBundleProvider> s = (Class<ResourceBundleProvider>) c;
1878                             return s;
1879                         }
1880                     } catch (ClassNotFoundException e) {}
1881                     return null;
1882                 }
1883             });
1884 
1885         if (service != null && Reflection.verifyModuleAccess(module, service)) {
1886             try {
1887                 return ServiceLoader.load(service, loader, module);
1888             } catch (ServiceConfigurationError e) {
1889                 // "uses" not declared: load bundle local in the module
1890                 return null;
1891             }
1892         }
1893         return null;
1894     }
1895 
1896     /**
1897      * Loads ResourceBundle from service providers.
1898      */
1899     private static ResourceBundle loadBundleFromProviders(String baseName,
1900                                                           Locale locale,
1901                                                           ServiceLoader<ResourceBundleProvider> providers,
1902                                                           CacheKey cacheKey)
1903     {
1904         if (providers == null) return null;
1905 
1906         return AccessController.doPrivileged(
1907                 new PrivilegedAction<>() {
1908                     public ResourceBundle run() {
1909                         for (Iterator<ResourceBundleProvider> itr = providers.iterator(); itr.hasNext(); ) {
1910                             try {
1911                                 ResourceBundleProvider provider = itr.next();
1912                                 if (cacheKey != null && cacheKey.callerHasProvider == null
1913                                         && cacheKey.getModule() == provider.getClass().getModule()) {
1914                                     cacheKey.callerHasProvider = Boolean.TRUE;
1915                                 }
1916                                 ResourceBundle bundle = provider.getBundle(baseName, locale);
1917                                 if (bundle != null) {
1918                                     return bundle;
1919                                 }
1920                             } catch (ServiceConfigurationError | SecurityException e) {
1921                                 if (cacheKey != null) {
1922                                     cacheKey.setCause(e);
1923                                 }
1924                             }
1925                         }
1926                         if (cacheKey != null && cacheKey.callerHasProvider == null) {
1927                             cacheKey.callerHasProvider = Boolean.FALSE;
1928                         }
1929                         return null;
1930                     }
1931                 });
1932 
1933     }
1934 
1935     /*
1936      * Legacy mechanism to load resource bundles
1937      */
1938     private static ResourceBundle loadBundle(CacheKey cacheKey,
1939                                              List<String> formats,
1940                                              Control control,
1941                                              boolean reload) {
1942 
1943         // Here we actually load the bundle in the order of formats
1944         // specified by the getFormats() value.
1945         Locale targetLocale = cacheKey.getLocale();
1946 
1947         ResourceBundle bundle = null;
1948         for (String format : formats) {
1949             try {
1950                 // ResourceBundle.Control.newBundle may be overridden
1951                 bundle = control.newBundle(cacheKey.getName(), targetLocale, format,
1952                                            cacheKey.getLoader(), reload);
1953             } catch (LinkageError | Exception error) {
1954                 // We need to handle the LinkageError case due to
1955                 // inconsistent case-sensitivity in ClassLoader.
1956                 // See 6572242 for details.
1957                 cacheKey.setCause(error);
1958             }
1959             if (bundle != null) {
1960                 // Set the format in the cache key so that it can be
1961                 // used when calling needsReload later.
1962                 cacheKey.setFormat(format);
1963                 bundle.name = cacheKey.getName();
1964                 bundle.locale = targetLocale;
1965                 // Bundle provider might reuse instances. So we should make
1966                 // sure to clear the expired flag here.
1967                 bundle.expired = false;
1968                 break;
1969             }
1970         }
1971 
1972         return bundle;
1973     }
1974 
1975     private static boolean isValidBundle(ResourceBundle bundle) {
1976         return bundle != null && bundle != NONEXISTENT_BUNDLE;
1977     }
1978 
1979     /**
1980      * Determines whether any of resource bundles in the parent chain,
1981      * including the leaf, have expired.
1982      */
1983     private static boolean hasValidParentChain(ResourceBundle bundle) {
1984         long now = System.currentTimeMillis();
1985         while (bundle != null) {
1986             if (bundle.expired) {
1987                 return false;
1988             }
1989             CacheKey key = bundle.cacheKey;
1990             if (key != null) {
1991                 long expirationTime = key.expirationTime;
1992                 if (expirationTime >= 0 && expirationTime <= now) {
1993                     return false;
1994                 }
1995             }
1996             bundle = bundle.parent;
1997         }
1998         return true;
1999     }
2000 
2001     /**
2002      * Throw a MissingResourceException with proper message
2003      */
2004     private static void throwMissingResourceException(String baseName,
2005                                                       Locale locale,
2006                                                       Throwable cause) {
2007         // If the cause is a MissingResourceException, avoid creating
2008         // a long chain. (6355009)
2009         if (cause instanceof MissingResourceException) {
2010             cause = null;
2011         }
2012         throw new MissingResourceException("Can't find bundle for base name "
2013                                            + baseName + ", locale " + locale,
2014                                            baseName + "_" + locale, // className
2015                                            "",                      // key
2016                                            cause);
2017     }
2018 
2019     /**
2020      * Finds a bundle in the cache. Any expired bundles are marked as
2021      * `expired' and removed from the cache upon return.
2022      *
2023      * @param cacheKey the key to look up the cache
2024      * @param control the Control to be used for the expiration control
2025      * @return the cached bundle, or null if the bundle is not found in the
2026      * cache or its parent has expired. <code>bundle.expire</code> is true
2027      * upon return if the bundle in the cache has expired.
2028      */
2029     private static ResourceBundle findBundleInCache(CacheKey cacheKey,
2030                                                     Control control) {
2031         BundleReference bundleRef = cacheList.get(cacheKey);
2032         if (bundleRef == null) {
2033             return null;
2034         }
2035         ResourceBundle bundle = bundleRef.get();
2036         if (bundle == null) {
2037             return null;
2038         }
2039         ResourceBundle p = bundle.parent;
2040         assert p != NONEXISTENT_BUNDLE;
2041         // If the parent has expired, then this one must also expire. We
2042         // check only the immediate parent because the actual loading is
2043         // done from the root (base) to leaf (child) and the purpose of
2044         // checking is to propagate expiration towards the leaf. For
2045         // example, if the requested locale is ja_JP_JP and there are
2046         // bundles for all of the candidates in the cache, we have a list,
2047         //
2048         // base <- ja <- ja_JP <- ja_JP_JP
2049         //
2050         // If ja has expired, then it will reload ja and the list becomes a
2051         // tree.
2052         //
2053         // base <- ja (new)
2054         //  "   <- ja (expired) <- ja_JP <- ja_JP_JP
2055         //
2056         // When looking up ja_JP in the cache, it finds ja_JP in the cache
2057         // which references to the expired ja. Then, ja_JP is marked as
2058         // expired and removed from the cache. This will be propagated to
2059         // ja_JP_JP.
2060         //
2061         // Now, it's possible, for example, that while loading new ja_JP,
2062         // someone else has started loading the same bundle and finds the
2063         // base bundle has expired. Then, what we get from the first
2064         // getBundle call includes the expired base bundle. However, if
2065         // someone else didn't start its loading, we wouldn't know if the
2066         // base bundle has expired at the end of the loading process. The
2067         // expiration control doesn't guarantee that the returned bundle and
2068         // its parents haven't expired.
2069         //
2070         // We could check the entire parent chain to see if there's any in
2071         // the chain that has expired. But this process may never end. An
2072         // extreme case would be that getTimeToLive returns 0 and
2073         // needsReload always returns true.
2074         if (p != null && p.expired) {
2075             assert bundle != NONEXISTENT_BUNDLE;
2076             bundle.expired = true;
2077             bundle.cacheKey = null;
2078             cacheList.remove(cacheKey, bundleRef);
2079             bundle = null;
2080         } else {
2081             CacheKey key = bundleRef.getCacheKey();
2082             long expirationTime = key.expirationTime;
2083             if (!bundle.expired && expirationTime >= 0 &&
2084                 expirationTime <= System.currentTimeMillis()) {
2085                 // its TTL period has expired.
2086                 if (bundle != NONEXISTENT_BUNDLE) {
2087                     // Synchronize here to call needsReload to avoid
2088                     // redundant concurrent calls for the same bundle.
2089                     synchronized (bundle) {
2090                         expirationTime = key.expirationTime;
2091                         if (!bundle.expired && expirationTime >= 0 &&
2092                             expirationTime <= System.currentTimeMillis()) {
2093                             try {
2094                                 bundle.expired = control.needsReload(key.getName(),
2095                                                                      key.getLocale(),
2096                                                                      key.getFormat(),
2097                                                                      key.getLoader(),
2098                                                                      bundle,
2099                                                                      key.loadTime);
2100                             } catch (Exception e) {
2101                                 cacheKey.setCause(e);
2102                             }
2103                             if (bundle.expired) {
2104                                 // If the bundle needs to be reloaded, then
2105                                 // remove the bundle from the cache, but
2106                                 // return the bundle with the expired flag
2107                                 // on.
2108                                 bundle.cacheKey = null;
2109                                 cacheList.remove(cacheKey, bundleRef);
2110                             } else {
2111                                 // Update the expiration control info. and reuse
2112                                 // the same bundle instance
2113                                 setExpirationTime(key, control);
2114                             }
2115                         }
2116                     }
2117                 } else {
2118                     // We just remove NONEXISTENT_BUNDLE from the cache.
2119                     cacheList.remove(cacheKey, bundleRef);
2120                     bundle = null;
2121                 }
2122             }
2123         }
2124         return bundle;
2125     }
2126 
2127     /**
2128      * Put a new bundle in the cache.
2129      *
2130      * @param cacheKey the key for the resource bundle
2131      * @param bundle the resource bundle to be put in the cache
2132      * @return the ResourceBundle for the cacheKey; if someone has put
2133      * the bundle before this call, the one found in the cache is
2134      * returned.
2135      */
2136     private static ResourceBundle putBundleInCache(CacheKey cacheKey,
2137                                                    ResourceBundle bundle,
2138                                                    Control control) {
2139         setExpirationTime(cacheKey, control);
2140         if (cacheKey.expirationTime != Control.TTL_DONT_CACHE) {
2141             CacheKey key = (CacheKey) cacheKey.clone();
2142             BundleReference bundleRef = new BundleReference(bundle, referenceQueue, key);
2143             bundle.cacheKey = key;


2144 
2145             // Put the bundle in the cache if it's not been in the cache.
2146             BundleReference result = cacheList.putIfAbsent(key, bundleRef);
2147 
2148             // If someone else has put the same bundle in the cache before
2149             // us and it has not expired, we should use the one in the cache.
2150             if (result != null) {
2151                 ResourceBundle rb = result.get();
2152                 if (rb != null && !rb.expired) {
2153                     // Clear the back link to the cache key
2154                     bundle.cacheKey = null;
2155                     bundle = rb;
2156                     // Clear the reference in the BundleReference so that
2157                     // it won't be enqueued.
2158                     bundleRef.clear();
2159                 } else {
2160                     // Replace the invalid (garbage collected or expired)
2161                     // instance with the valid one.
2162                     cacheList.put(key, bundleRef);






2163                 }
2164             }
2165         }
2166         return bundle;
2167     }
2168 
2169     private static void setExpirationTime(CacheKey cacheKey, Control control) {
2170         long ttl = control.getTimeToLive(cacheKey.getName(),
2171                                          cacheKey.getLocale());


2172         if (ttl >= 0) {
2173             // If any expiration time is specified, set the time to be
2174             // expired in the cache.
2175             long now = System.currentTimeMillis();
2176             cacheKey.loadTime = now;
2177             cacheKey.expirationTime = now + ttl;
2178         } else if (ttl >= Control.TTL_NO_EXPIRATION_CONTROL) {
2179             cacheKey.expirationTime = ttl;
2180         } else {
2181             throw new IllegalArgumentException("Invalid Control: TTL=" + ttl);
2182         }
2183     }
2184 
2185     /**
2186      * Removes all resource bundles from the cache that have been loaded
2187      * by the caller's module using the caller's class loader.
2188      *
2189      * @since 1.6
2190      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
2191      */
2192     @CallerSensitive
2193     public static final void clearCache() {
2194         Class<?> caller = Reflection.getCallerClass();
2195         clearCache(getLoader(caller), caller.getModule());
2196     }
2197 
2198     /**
2199      * Removes all resource bundles from the cache that have been loaded


2212 
2213     /**
2214      * Removes all resource bundles from the cache that have been loaded by the
2215      * given {@code module}.
2216      *
2217      * @param module the module
2218      * @throws NullPointerException
2219      *         if {@code module} is {@code null}
2220      * @throws SecurityException
2221      *         if the caller doesn't have the permission to
2222      *         {@linkplain Module#getClassLoader() get the class loader}
2223      *         of the given {@code module}
2224      * @since 9
2225      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
2226      */
2227     public static final void clearCache(Module module) {
2228         clearCache(module.getClassLoader(), module);
2229     }
2230 
2231     private static void clearCache(ClassLoader loader, Module module) {
2232         Set<CacheKey> set = cacheList.keySet();
2233         set.stream().filter((key) -> (key.getLoader() == loader && key.getModule() == module))
2234                 .forEach(set::remove);
2235     }
2236 
2237     /**
2238      * Gets an object for the given key from this resource bundle.
2239      * Returns null if this resource bundle does not contain an
2240      * object for the given key.
2241      *
2242      * @param key the key for the desired object
2243      * @exception NullPointerException if <code>key</code> is <code>null</code>
2244      * @return the object for the given key, or null
2245      */
2246     protected abstract Object handleGetObject(String key);
2247 
2248     /**
2249      * Returns an enumeration of the keys.
2250      *
2251      * @return an <code>Enumeration</code> of the keys contained in
2252      *         this <code>ResourceBundle</code> and its parent bundles.
2253      */
2254     public abstract Enumeration<String> getKeys();




  27  * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
  28  * (C) Copyright IBM Corp. 1996 - 1999 - All Rights Reserved
  29  *
  30  * The original version of this source code and documentation
  31  * is copyrighted and owned by Taligent, Inc., a wholly-owned
  32  * subsidiary of IBM. These materials are provided under terms
  33  * of a License Agreement between Taligent and Sun. This technology
  34  * is protected by multiple US and International patents.
  35  *
  36  * This notice and attribution to Taligent may not be removed.
  37  * Taligent is a registered trademark of Taligent, Inc.
  38  *
  39  */
  40 
  41 package java.util;
  42 
  43 import java.io.IOException;
  44 import java.io.InputStream;
  45 import java.lang.ref.ReferenceQueue;
  46 import java.lang.ref.SoftReference;

  47 import java.lang.reflect.Constructor;
  48 import java.lang.reflect.InvocationTargetException;
  49 import java.lang.reflect.Modifier;
  50 import java.lang.reflect.Module;
  51 import java.net.JarURLConnection;
  52 import java.net.URL;
  53 import java.net.URLConnection;
  54 import java.security.AccessController;
  55 import java.security.PrivilegedAction;
  56 import java.security.PrivilegedActionException;
  57 import java.security.PrivilegedExceptionAction;


  58 import java.util.jar.JarEntry;
  59 import java.util.spi.ResourceBundleControlProvider;
  60 import java.util.spi.ResourceBundleProvider;
  61 
  62 import jdk.internal.misc.JavaUtilResourceBundleAccess;
  63 import jdk.internal.misc.SharedSecrets;
  64 import jdk.internal.reflect.CallerSensitive;
  65 import jdk.internal.reflect.Reflection;
  66 import jdk.internal.util.concurrent.AbstractClassLoaderValue;
  67 import jdk.internal.util.concurrent.ClassLoaderValue;
  68 import sun.util.locale.BaseLocale;
  69 import sun.util.locale.LocaleObjectCache;
  70 import sun.util.locale.provider.ResourceBundleProviderSupport;
  71 import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;
  72 
  73 
  74 /**
  75  *
  76  * Resource bundles contain locale-specific objects.  When your program needs a
  77  * locale-specific resource, a <code>String</code> for example, your program can
  78  * load it from the resource bundle that is appropriate for the current user's
  79  * locale. In this way, you can write program code that is largely independent
  80  * of the user's locale isolating most, if not all, of the locale-specific
  81  * information in resource bundles.
  82  *
  83  * <p>
  84  * This allows you to write programs that can:
  85  * <UL>
  86  * <LI> be easily localized, or translated, into different languages
  87  * <LI> handle multiple locales at once


 328  *         return new HashSet&lt;String&gt;(Arrays.asList("cancelKey"));
 329  *     }
 330  * }
 331  * </pre>
 332  * </blockquote>
 333  * You do not have to restrict yourself to using a single family of
 334  * <code>ResourceBundle</code>s. For example, you could have a set of bundles for
 335  * exception messages, <code>ExceptionResources</code>
 336  * (<code>ExceptionResources_fr</code>, <code>ExceptionResources_de</code>, ...),
 337  * and one for widgets, <code>WidgetResource</code> (<code>WidgetResources_fr</code>,
 338  * <code>WidgetResources_de</code>, ...); breaking up the resources however you like.
 339  *
 340  * @see ListResourceBundle
 341  * @see PropertyResourceBundle
 342  * @see MissingResourceException
 343  * @see ResourceBundleProvider
 344  * @since 1.1
 345  */
 346 public abstract class ResourceBundle {
 347 



 348     static {
 349         SharedSecrets.setJavaUtilResourceBundleAccess(
 350             new JavaUtilResourceBundleAccess() {
 351                 @Override
 352                 public void setParent(ResourceBundle bundle,
 353                                       ResourceBundle parent) {
 354                     bundle.setParent(parent);
 355                 }
 356 
 357                 @Override
 358                 public ResourceBundle getParent(ResourceBundle bundle) {
 359                     return bundle.parent;
 360                 }
 361 
 362                 @Override
 363                 public void setLocale(ResourceBundle bundle, Locale locale) {
 364                     bundle.locale = locale;
 365                 }
 366 
 367                 @Override
 368                 public void setName(ResourceBundle bundle, String name) {
 369                     bundle.name = name;
 370                 }
 371             });
 372     }
 373 
 374     /**
 375      * special bundle type indicating that no resource bundle exists
 376      */
 377     private static final class NONEXISTENT_BUNDLE extends ResourceBundle {
 378         public Enumeration<String> getKeys() { return null; }
 379         protected Object handleGetObject(String key) { return null; }
 380         public String toString() { return "NONEXISTENT_BUNDLE"; }
 381     }
 382 
 383 
 384     /**
 385      * The cache of BundleReference(s) per class loader, module, bundle base name
 386      * and locale.








 387      */
 388     private static final ClassLoaderValue<BundleReference> cache
 389         = new ClassLoaderValue<>();
 390 
 391     /**
 392      * Queue for reference objects referring to class loaders or bundles.
 393      */
 394     private static final ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
 395 
 396     /**
 397      * Returns the base name of this bundle, if known, or {@code null} if unknown.
 398      *
 399      * If not null, then this is the value of the {@code baseName} parameter
 400      * that was passed to the {@code ResourceBundle.getBundle(...)} method
 401      * when the resource bundle was loaded.
 402      *
 403      * @return The base name of the resource bundle, as provided to and expected
 404      * by the {@code ResourceBundle.getBundle(...)} methods.
 405      *
 406      * @see #getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader)
 407      *
 408      * @since 1.8
 409      */
 410     public String getBaseBundleName() {
 411         return name;
 412     }
 413 
 414     /**
 415      * The parent bundle of this bundle.
 416      * The parent bundle is searched by {@link #getObject getObject}
 417      * when this bundle does not contain a particular resource.
 418      */
 419     protected ResourceBundle parent;
 420 
 421     /**
 422      * The locale for this bundle.
 423      */
 424     private Locale locale;
 425 
 426     /**
 427      * The base bundle name for this bundle.
 428      */
 429     private String name;
 430 
 431     /**
 432      * Bundle format which is necessary for calling
 433      * Control.needsReload().
 434      */
 435     private String format;
 436 
 437     /**
 438      * The flag indicating this bundle has expired in the cache.
 439      */
 440     private volatile boolean expired;
 441 
 442     /**
 443      * The time when the bundle has been loaded

 444      */
 445     private volatile long loadTime;
 446 
 447     /**
 448      * The time when the bundle expires in the cache, or either
 449      * Control.TTL_DONT_CACHE or Control.TTL_NO_EXPIRATION_CONTROL.
 450      */
 451     private volatile long expirationTime;
 452 
 453     /**
 454      * A Set of the keys contained only in this ResourceBundle.
 455      */
 456     private volatile Set<String> keySet;
 457 
 458     private static final List<ResourceBundleControlProvider> providers;
 459 
 460     static {
 461         List<ResourceBundleControlProvider> list = null;
 462         ServiceLoader<ResourceBundleControlProvider> serviceLoaders
 463                 = ServiceLoader.loadInstalled(ResourceBundleControlProvider.class);
 464         for (ResourceBundleControlProvider provider : serviceLoaders) {
 465             if (list == null) {
 466                 list = new ArrayList<>();
 467             }
 468             list.add(provider);
 469         }
 470         providers = list;
 471     }


 602             }
 603             return ClassLoader.getSystemResource(name);
 604         }
 605         public InputStream getResourceAsStream(String name) {
 606             ClassLoader loader = ClassLoader.getSystemClassLoader();
 607             if (loader != null) {
 608                 return loader.getResourceAsStream(name);
 609             }
 610             return ClassLoader.getSystemResourceAsStream(name);
 611         }
 612     }
 613 
 614     /**
 615      * Sets the parent bundle of this bundle.
 616      * The parent bundle is searched by {@link #getObject getObject}
 617      * when this bundle does not contain a particular resource.
 618      *
 619      * @param parent this bundle's parent bundle.
 620      */
 621     protected void setParent(ResourceBundle parent) {
 622         assert !(parent instanceof NONEXISTENT_BUNDLE);
 623         this.parent = parent;
 624     }
 625 
 626     /**
 627      * A session object used during the {@link #getBundle} call.
 628      * The loader may be null (in which case it is considered to be the
 629      * bootstrap class loader), but the module, base name, and locale must have
 630      * a non-null value.
 631      */
 632     private static class LoadSession {
 633         // These four are the actual keys for lookup in cache.
 634         private ClassLoader loader;
 635         private Module module;
 636         private String name;
 637         private Locale locale;










 638 
 639         // compose and return the cache key
 640         ClassLoaderValue<BundleReference>.Sub<Module>.Sub<String>.Sub<Locale> key() {
 641             return cache.sub(module).sub(name).sub(locale);
 642         }
 643 
 644         // return a bundle reference from cache or null
 645         BundleReference getFromCache() {
 646             return key().get(loader);
 647         }
 648 
 649         // Placeholder for an error report by a Throwable
 650         private Throwable cause;
 651 




 652         // ResourceBundleProviders for loading ResourceBundles
 653         private Iterable<ResourceBundleProvider> providers;

 654 
 655         // Boolean.TRUE if the factory method caller provides a ResourceBundleProvier.
 656         private Boolean callerHasProvider;
 657 
 658         LoadSession(String baseName, Locale locale, ClassLoader loader, Module module) {
 659             this.name = Objects.requireNonNull(baseName);
 660             this.locale = Objects.requireNonNull(locale);
 661             this.loader = loader;
 662             this.module = Objects.requireNonNull(module);







 663         }
 664 
 665         String getName() {
 666             return name;
 667         }
 668 
 669         LoadSession setName(String baseName) {
 670             this.name = Objects.requireNonNull(baseName);



 671             return this;
 672         }
 673 
 674         Locale getLocale() {
 675             return locale;
 676         }
 677 
 678         LoadSession setLocale(Locale locale) {
 679             this.locale = Objects.requireNonNull(locale);



 680             return this;
 681         }
 682 
 683         ClassLoader getLoader() {
 684             return loader;
 685         }
 686 
 687         Module getModule() {
 688             return module;
 689         }
 690 
 691         Iterable<ResourceBundleProvider> getProviders() {
 692             if (providers == null) {
 693                 providers = ResourceBundle.getProviders(module, name);

 694             }
 695             return providers;
 696         }
 697 




 698         boolean callerHasProvider() {
 699             return callerHasProvider == Boolean.TRUE;
 700         }
 701 
























































































 702         private void setCause(Throwable cause) {
 703             if (this.cause == null) {
 704                 this.cause = cause;
 705             } else {
 706                 // Override the cause if the previous one is
 707                 // ClassNotFoundException.
 708                 if (this.cause instanceof ClassNotFoundException) {
 709                     this.cause = cause;
 710                 }
 711             }
 712         }
 713 
 714         private Throwable getCause() {
 715             return cause;
 716         }
 717 
 718         @Override
 719         public String toString() {
 720             String l = locale.toString();
 721             if (l.length() == 0) {
 722                 if (locale.getVariant().length() != 0) {
 723                     l = "__" + locale.getVariant();
 724                 } else {
 725                     l = "\"\"";
 726                 }
 727             }
 728             return "LookupSession[" + name + ", lc=" + l + ", ldr=" + getLoader()
 729                 + "]";
 730         }
 731     }
 732 
 733     /**
 734      * References to bundles are soft references so that they can be cleared
 735      * when GC demands to free some heap.








 736      */
 737     private static class BundleReference extends SoftReference<ResourceBundle> {
 738         private final ClassLoader classLoader;
 739         private final AbstractClassLoaderValue<?, BundleReference>.Sub<?> key;
 740 
 741         BundleReference(ResourceBundle referent, ReferenceQueue<Object> q,
 742                         ClassLoader classLoader,
 743                         AbstractClassLoaderValue<?, BundleReference>.Sub<?> key) {
 744             super(referent, q);
 745             this.classLoader = classLoader;
 746             this.key = key;
 747         }
 748 
 749         void remove() {
 750             key.remove(classLoader, this);




















 751         }
 752     }
 753 
 754     /**
 755      * Gets a resource bundle using the specified base name, the default locale,
 756      * and the caller's class loader. Calling this method is equivalent to calling
 757      * <blockquote>
 758      * <code>getBundle(baseName, Locale.getDefault(), this.getClass().getClassLoader())</code>,
 759      * </blockquote>
 760      * except that <code>getClassLoader()</code> is run with the security
 761      * privileges of <code>ResourceBundle</code>.
 762      * See {@link #getBundle(String, Locale, ClassLoader) getBundle}
 763      * for a complete description of the search and instantiation strategy.
 764      *
 765      * @param baseName the base name of the resource bundle, a fully qualified class name
 766      * @exception java.lang.NullPointerException
 767      *     if <code>baseName</code> is <code>null</code>
 768      * @exception MissingResourceException
 769      *     if no resource bundle for the specified base name can be found
 770      * @return a resource bundle for the given base name and the default locale


1462                                                       Control control) {
1463         Objects.requireNonNull(module);
1464         if (caller.getModule() != module) {
1465             SecurityManager sm = System.getSecurityManager();
1466             if (sm != null) {
1467                 sm.checkPermission(GET_CLASSLOADER_PERMISSION);
1468             }
1469         }
1470         return getBundleImpl(baseName, locale, getLoader(module), module, control);
1471     }
1472 
1473     private static ResourceBundle getBundleImpl(String baseName,
1474                                                 Locale locale,
1475                                                 ClassLoader loader,
1476                                                 Module module,
1477                                                 Control control) {
1478         if (locale == null || control == null) {
1479             throw new NullPointerException();
1480         }
1481 
1482         // We create a LookupSession here for use by this call. The base name
1483         // loader, and module will never change during the bundle loading
1484         // process.
1485         LoadSession loadSession = new LoadSession(baseName, locale, loader, module);


1486 
1487         // Quick lookup of the cache.
1488         ResourceBundle bundle = null;
1489         BundleReference bundleRef = loadSession.getFromCache();
1490         if (bundleRef != null) {
1491             bundle = bundleRef.get();

1492         }
1493 
1494         // If this bundle and all of its parents are valid (not expired),
1495         // then return this bundle. If any of the bundles is expired, we
1496         // don't call control.needsReload here but instead drop into the
1497         // complete loading process below.
1498         if (isValidBundle(bundle) && hasValidParentChain(bundle)) {
1499             return bundle;
1500         }
1501 
1502         // No valid bundle was found in the cache, so we need to load the
1503         // resource bundle and its parents.
1504 
1505         boolean isKnownControl = (control == Control.INSTANCE) ||
1506                                    (control instanceof SingleFormatControl);
1507         List<String> formats = control.getFormats(baseName);
1508         if (!isKnownControl && !checkList(formats)) {
1509             throw new IllegalArgumentException("Invalid Control: getFormats");
1510         }
1511 
1512         ResourceBundle baseBundle = null;
1513         for (Locale targetLocale = locale;
1514              targetLocale != null;
1515              targetLocale = control.getFallbackLocale(baseName, targetLocale)) {
1516             List<Locale> candidateLocales = control.getCandidateLocales(baseName, targetLocale);
1517             if (!isKnownControl && !checkList(candidateLocales)) {
1518                 throw new IllegalArgumentException("Invalid Control: getCandidateLocales");
1519             }
1520 
1521             bundle = findBundle(loadSession, module, candidateLocales, formats, 0, control, baseBundle);
1522 
1523             // If the loaded bundle is the base bundle and exactly for the
1524             // requested locale or the only candidate locale, then take the
1525             // bundle as the resulting one. If the loaded bundle is the base
1526             // bundle, it's put on hold until we finish processing all
1527             // fallback locales.
1528             if (isValidBundle(bundle)) {
1529                 boolean isBaseBundle = Locale.ROOT.equals(bundle.locale);
1530                 if (!isBaseBundle || bundle.locale.equals(locale)
1531                     || (candidateLocales.size() == 1
1532                         && bundle.locale.equals(candidateLocales.get(0)))) {
1533                     break;
1534                 }
1535 
1536                 // If the base bundle has been loaded, keep the reference in
1537                 // baseBundle so that we can avoid any redundant loading in case
1538                 // the control specify not to cache bundles.
1539                 if (isBaseBundle && baseBundle == null) {
1540                     baseBundle = bundle;
1541                 }
1542             }
1543         }
1544 
1545         if (bundle == null) {
1546             if (baseBundle == null) {
1547                 throwMissingResourceException(baseName, locale, loadSession.getCause());
1548             }
1549             bundle = baseBundle;
1550         }
1551 
1552         return bundle;
1553     }
1554 
1555     /**
1556      * Checks if the given <code>List</code> is not null, not empty,
1557      * not having null in its elements.
1558      */
1559     private static boolean checkList(List<?> a) {
1560         boolean valid = (a != null && !a.isEmpty());
1561         if (valid) {
1562             int size = a.size();
1563             for (int i = 0; valid && i < size; i++) {
1564                 valid = (a.get(i) != null);
1565             }
1566         }
1567         return valid;
1568     }
1569 
1570     private static ResourceBundle findBundle(LoadSession loadSession,
1571                                              Module module,
1572                                              List<Locale> candidateLocales,
1573                                              List<String> formats,
1574                                              int index,
1575                                              Control control,
1576                                              ResourceBundle baseBundle) {
1577         Locale targetLocale = candidateLocales.get(index);
1578         ResourceBundle parent = null;
1579         if (index != candidateLocales.size() - 1) {
1580             parent = findBundle(loadSession, module, candidateLocales, formats, index + 1,
1581                                 control, baseBundle);
1582         } else if (baseBundle != null && Locale.ROOT.equals(targetLocale)) {
1583             return baseBundle;
1584         }
1585 
1586         // Before we do the real loading work, see whether we need to
1587         // do some housekeeping: If soft references to resource bundles
1588         // have been nulled out, remove all related information from the cache.

1589         Object ref;
1590         while ((ref = referenceQueue.poll()) != null) {
1591             ((BundleReference) ref).remove();
1592         }
1593 
1594         // flag indicating the resource bundle has expired in the cache
1595         boolean expiredBundle = false;
1596 
1597         // First, look up the cache to see if it's in the cache, without
1598         // attempting to load bundle.
1599         loadSession.setLocale(targetLocale);
1600         ResourceBundle bundle = findBundleInCache(loadSession, control);
1601         if (isValidBundle(bundle)) {
1602             expiredBundle = bundle.expired;
1603             if (!expiredBundle) {
1604                 // If its parent is the one asked for by the candidate
1605                 // locales (the runtime lookup path), we can take the cached
1606                 // one. (If it's not identical, then we'd have to check the
1607                 // parent's parents to be consistent with what's been
1608                 // requested.)
1609                 if (bundle.parent == parent) {
1610                     return bundle;
1611                 }
1612                 // Otherwise, remove the cached one since we can't keep
1613                 // the same bundles having different parents.
1614                 BundleReference bundleRef = loadSession.getFromCache();
1615                 if (bundleRef != null && bundleRef.get() == bundle) {
1616                     bundleRef.remove();
1617                 }
1618             }
1619         }
1620 
1621         if (!(bundle instanceof NONEXISTENT_BUNDLE)) {



1622             if (module.isNamed()) {
1623                 bundle = loadBundle(loadSession, formats, control, module);
1624             } else {
1625                 bundle = loadBundle(loadSession, formats, control, expiredBundle);
1626             }
1627             if (bundle != null) {
1628                 if (bundle.parent == null) {
1629                     bundle.setParent(parent);
1630                 }
1631                 bundle.locale = targetLocale;
1632                 bundle = putBundleInCache(loadSession, bundle, control);
1633                 return bundle;
1634             }
1635 
1636             // Put NONEXISTENT_BUNDLE in the cache as a mark that there's no bundle
1637             // instance for the locale.
1638             putBundleInCache(loadSession, new NONEXISTENT_BUNDLE(), control);





1639         }
1640         return parent;
1641     }
1642 
1643     private static final String UNKNOWN_FORMAT = "";
1644 
1645     /*
1646      * Loads a ResourceBundle in named modules
1647      */
1648     private static ResourceBundle loadBundle(LoadSession loadSession,
1649                                              List<String> formats,
1650                                              Control control,
1651                                              Module module) {
1652         String baseName = loadSession.getName();
1653         Locale targetLocale = loadSession.getLocale();
1654 
1655         ResourceBundle bundle = null;
1656         Iterable<ResourceBundleProvider> providers = loadSession.getProviders();
1657         if (providers != NO_PROVIDERS) {
1658              bundle = loadBundleFromProviders(baseName, targetLocale,
1659                                               providers, loadSession);
1660             if (bundle != null) {
1661                 bundle.format = UNKNOWN_FORMAT;
1662             }
1663         }
1664         // If none of providers returned a bundle and the caller has no provider,
1665         // look up module-local bundles.
1666         if (bundle == null && !loadSession.callerHasProvider()) {
1667             String bundleName = control.toBundleName(baseName, targetLocale);
1668             for (String format : formats) {
1669                 try {
1670                     switch (format) {
1671                     case "java.class":
1672                         PrivilegedAction<ResourceBundle> pa = ()
1673                                 -> ResourceBundleProviderSupport
1674                                     .loadResourceBundle(module, bundleName);
1675                         bundle = AccessController.doPrivileged(pa, null, GET_CLASSLOADER_PERMISSION);
1676                         break;
1677                     case "java.properties":
1678                         bundle = ResourceBundleProviderSupport.loadPropertyResourceBundle(module, bundleName);
1679                         break;
1680                     default:
1681                         throw new InternalError("unexpected format: " + format);
1682                     }
1683 
1684                     if (bundle != null) {
1685                         bundle.format = format;
1686                         break;
1687                     }
1688                 } catch (Exception e) {
1689                     loadSession.setCause(e);
1690                 }
1691             }
1692         }
1693         return bundle;
1694     }
1695 
1696     // An instance that signals getting providers for unnamed module or
1697     // inability to get providers as opposed to successfully getting 0 providers
1698     // from a named module.
1699     private static final Iterable<ResourceBundleProvider> NO_PROVIDERS =
1700         Collections.emptyList();
1701 
1702     private static Iterable<ResourceBundleProvider> getProviders(Module module,
1703                                                                  String baseName) {
1704         if (!module.isNamed()) {
1705              return NO_PROVIDERS;
1706         }
1707         PrivilegedAction<ClassLoader> pa = module::getClassLoader;
1708         ClassLoader loader = AccessController.doPrivileged(pa);
1709         return getProviders(module, loader, baseName);
1710     }
1711 
1712     /**
1713      * Returns a ServiceLoader that will find providers that are bound to
1714      * a given module that may be named or unnamed or GET_PROVIDERS_FAILED instance
1715      * if unsuccessful.
1716      */
1717     private static Iterable<ResourceBundleProvider> getProviders(Module module,
1718                                                                  ClassLoader loader,
1719                                                                  String baseName)
1720     {
1721         // Look up <baseName> + "Provider"
1722         String providerName = baseName + "Provider";
1723         // Use the class loader of the getBundle caller so that the caller's
1724         // visibility of the provider type is checked.
1725         Class<ResourceBundleProvider> service = AccessController.doPrivileged(
1726             new PrivilegedAction<>() {
1727                 @Override
1728                 public Class<ResourceBundleProvider> run() {
1729                     try {
1730                         Class<?> c = Class.forName(providerName, false, loader);
1731                         if (ResourceBundleProvider.class.isAssignableFrom(c)) {
1732                             @SuppressWarnings("unchecked")
1733                             Class<ResourceBundleProvider> s = (Class<ResourceBundleProvider>) c;
1734                             return s;
1735                         }
1736                     } catch (ClassNotFoundException e) {}
1737                     return null;
1738                 }
1739             });
1740 
1741         if (service != null && Reflection.verifyModuleAccess(module, service)) {
1742             try {
1743                 return ServiceLoader.load(service, loader, module);
1744             } catch (ServiceConfigurationError e) {
1745                 // "uses" not declared: load bundle local in the module

1746             }
1747         }
1748         return NO_PROVIDERS;
1749     }
1750 
1751     /**
1752      * Loads ResourceBundle from service providers.
1753      */
1754     private static ResourceBundle loadBundleFromProviders(String baseName,
1755                                                           Locale locale,
1756                                                           Iterable<ResourceBundleProvider> providers,
1757                                                           LoadSession loadSession)
1758     {
1759         // assert loadSession != null && providers != null;

1760         return AccessController.doPrivileged(
1761                 new PrivilegedAction<>() {
1762                     public ResourceBundle run() {
1763                         for (ResourceBundleProvider provider : providers) {
1764                             try {
1765                                 if (loadSession.callerHasProvider == null &&
1766                                     loadSession.getModule() == provider.getClass().getModule())
1767                                 {
1768                                     loadSession.callerHasProvider = Boolean.TRUE;
1769                                 }
1770                                 ResourceBundle bundle = provider.getBundle(baseName, locale);
1771                                 if (bundle != null) {
1772                                     return bundle;
1773                                 }
1774                             } catch (ServiceConfigurationError | SecurityException e) {
1775                                 loadSession.setCause(e);

1776                             }
1777                         }
1778                         if (loadSession.callerHasProvider == null) {
1779                             loadSession.callerHasProvider = Boolean.FALSE;

1780                         }
1781                         return null;
1782                     }
1783                 });
1784 
1785     }
1786 
1787     /*
1788      * Legacy mechanism to load resource bundles
1789      */
1790     private static ResourceBundle loadBundle(LoadSession loadSession,
1791                                              List<String> formats,
1792                                              Control control,
1793                                              boolean reload) {
1794 
1795         // Here we actually load the bundle in the order of formats
1796         // specified by the getFormats() value.
1797         Locale targetLocale = loadSession.getLocale();
1798 
1799         ResourceBundle bundle = null;
1800         for (String format : formats) {
1801             try {
1802                 // ResourceBundle.Control.newBundle may be overridden
1803                 bundle = control.newBundle(loadSession.getName(), targetLocale, format,
1804                                            loadSession.getLoader(), reload);
1805             } catch (LinkageError | Exception error) {
1806                 // We need to handle the LinkageError case due to
1807                 // inconsistent case-sensitivity in ClassLoader.
1808                 // See 6572242 for details.
1809                 loadSession.setCause(error);
1810             }
1811             if (bundle != null) {
1812                 // Set the format in the cache key so that it can be
1813                 // used when calling needsReload later.
1814                 bundle.format = format;
1815                 bundle.name = loadSession.getName();
1816                 bundle.locale = targetLocale;
1817                 // Bundle provider might reuse instances. So we should make
1818                 // sure to clear the expired flag here.
1819                 bundle.expired = false;
1820                 break;
1821             }
1822         }
1823 
1824         return bundle;
1825     }
1826 
1827     private static boolean isValidBundle(ResourceBundle bundle) {
1828         return bundle != null && !(bundle instanceof NONEXISTENT_BUNDLE);
1829     }
1830 
1831     /**
1832      * Determines whether any of resource bundles in the parent chain,
1833      * including the leaf, have expired.
1834      */
1835     private static boolean hasValidParentChain(ResourceBundle bundle) {
1836         long now = System.currentTimeMillis();
1837         while (bundle != null) {
1838             if (bundle.expired) {
1839                 return false;
1840             }
1841             long expirationTime = bundle.expirationTime;


1842             if (expirationTime >= 0 && expirationTime <= now) {
1843                 return false;
1844             }

1845             bundle = bundle.parent;
1846         }
1847         return true;
1848     }
1849 
1850     /**
1851      * Throw a MissingResourceException with proper message
1852      */
1853     private static void throwMissingResourceException(String baseName,
1854                                                       Locale locale,
1855                                                       Throwable cause) {
1856         // If the cause is a MissingResourceException, avoid creating
1857         // a long chain. (6355009)
1858         if (cause instanceof MissingResourceException) {
1859             cause = null;
1860         }
1861         throw new MissingResourceException("Can't find bundle for base name "
1862                                            + baseName + ", locale " + locale,
1863                                            baseName + "_" + locale, // className
1864                                            "",                      // key
1865                                            cause);
1866     }
1867 
1868     /**
1869      * Finds a bundle in the cache. Any expired bundles are marked as
1870      * `expired' and removed from the cache upon return.
1871      *
1872      * @param loadSession the load session used look up the cache
1873      * @param control the Control to be used for the expiration control
1874      * @return the cached bundle, or null if the bundle is not found in the
1875      * cache or its parent has expired. <code>bundle.expire</code> is true
1876      * upon return if the bundle in the cache has expired.
1877      */
1878     private static ResourceBundle findBundleInCache(LoadSession loadSession,
1879                                                     Control control) {
1880         BundleReference bundleRef = loadSession.getFromCache();
1881         if (bundleRef == null) {
1882             return null;
1883         }
1884         ResourceBundle bundle = bundleRef.get();
1885         if (bundle == null) {
1886             return null;
1887         }
1888         ResourceBundle p = bundle.parent;
1889         assert !(p instanceof NONEXISTENT_BUNDLE);
1890         // If the parent has expired, then this one must also expire. We
1891         // check only the immediate parent because the actual loading is
1892         // done from the root (base) to leaf (child) and the purpose of
1893         // checking is to propagate expiration towards the leaf. For
1894         // example, if the requested locale is ja_JP_JP and there are
1895         // bundles for all of the candidates in the cache, we have a list,
1896         //
1897         // base <- ja <- ja_JP <- ja_JP_JP
1898         //
1899         // If ja has expired, then it will reload ja and the list becomes a
1900         // tree.
1901         //
1902         // base <- ja (new)
1903         //  "   <- ja (expired) <- ja_JP <- ja_JP_JP
1904         //
1905         // When looking up ja_JP in the cache, it finds ja_JP in the cache
1906         // which references to the expired ja. Then, ja_JP is marked as
1907         // expired and removed from the cache. This will be propagated to
1908         // ja_JP_JP.
1909         //
1910         // Now, it's possible, for example, that while loading new ja_JP,
1911         // someone else has started loading the same bundle and finds the
1912         // base bundle has expired. Then, what we get from the first
1913         // getBundle call includes the expired base bundle. However, if
1914         // someone else didn't start its loading, we wouldn't know if the
1915         // base bundle has expired at the end of the loading process. The
1916         // expiration control doesn't guarantee that the returned bundle and
1917         // its parents haven't expired.
1918         //
1919         // We could check the entire parent chain to see if there's any in
1920         // the chain that has expired. But this process may never end. An
1921         // extreme case would be that getTimeToLive returns 0 and
1922         // needsReload always returns true.
1923         if (p != null && p.expired) {
1924             assert !(bundle instanceof NONEXISTENT_BUNDLE);
1925             bundle.expired = true;
1926             bundleRef.remove();

1927             bundle = null;
1928         } else {
1929             long expirationTime = bundle.expirationTime;

1930             if (!bundle.expired && expirationTime >= 0 &&
1931                 expirationTime <= System.currentTimeMillis()) {
1932                 // its TTL period has expired.
1933                 if (!(bundle instanceof NONEXISTENT_BUNDLE)) {
1934                     // Synchronize here to call needsReload to avoid
1935                     // redundant concurrent calls for the same bundle.
1936                     synchronized (bundle) {
1937                         expirationTime = bundle.expirationTime;
1938                         if (!bundle.expired && expirationTime >= 0 &&
1939                             expirationTime <= System.currentTimeMillis()) {
1940                             try {
1941                                 bundle.expired = control.needsReload(bundle.getBaseBundleName(),
1942                                                                      bundle.getLocale(),
1943                                                                      bundle.format,
1944                                                                      loadSession.getLoader(),
1945                                                                      bundle,
1946                                                                      bundle.loadTime);
1947                             } catch (Exception e) {
1948                                 loadSession.setCause(e);
1949                             }
1950                             if (bundle.expired) {
1951                                 // If the bundle needs to be reloaded, then
1952                                 // remove the bundle from the cache, but
1953                                 // return the bundle with the expired flag
1954                                 // on.
1955                                 bundleRef.remove();

1956                             } else {
1957                                 // Update the expiration control info. and reuse
1958                                 // the same bundle instance
1959                                 setExpirationTime(bundle, loadSession, control);
1960                             }
1961                         }
1962                     }
1963                 } else {
1964                     // We just remove NONEXISTENT_BUNDLE from the cache.
1965                     bundleRef.remove();
1966                     bundle = null;
1967                 }
1968             }
1969         }
1970         return bundle;
1971     }
1972 
1973     /**
1974      * Put a new bundle in the cache.
1975      *
1976      * @param loadSession the key for the resource bundle
1977      * @param bundle the resource bundle to be put in the cache
1978      * @return the ResourceBundle for the cacheKey; if someone has put
1979      * the bundle before this call, the one found in the cache is
1980      * returned.
1981      */
1982     private static ResourceBundle putBundleInCache(LoadSession loadSession,
1983                                                    ResourceBundle bundle,
1984                                                    Control control) {
1985         setExpirationTime(bundle, loadSession, control);
1986         if (bundle.expirationTime != Control.TTL_DONT_CACHE) {
1987             ClassLoaderValue<BundleReference>.Sub<Module>.Sub<String>.Sub<Locale> key
1988                 = loadSession.key();
1989             ClassLoader loader = loadSession.getLoader();
1990             BundleReference bundleRef = new BundleReference(
1991                 bundle, referenceQueue, loader, key);
1992 
1993             // Put the bundle in the cache if it's not been in the cache.
1994             BundleReference oldBundleRef = key.putIfAbsent(loader, bundleRef);
1995 
1996             // If someone else has put the same bundle in the cache before
1997             // us and it has not expired, we should use the one in the cache.
1998             while (oldBundleRef != null) {
1999                 ResourceBundle rb = oldBundleRef.get();
2000                 if (rb != null && !rb.expired) {
2001                     return rb;





2002                 } else {
2003                     // Try to replace the invalid (garbage collected or expired)
2004                     // instance with the valid one.
2005                     if (key.replace(loader, oldBundleRef, bundleRef)) {
2006                         break;
2007                     } else {
2008                         // Someone else must have already replaced it or it was
2009                         // removed. Retry putting the bundle in the cache.
2010                         oldBundleRef = key.putIfAbsent(loader, bundleRef);
2011                     }
2012                 }
2013             }
2014         }
2015         return bundle;
2016     }
2017 
2018     private static void setExpirationTime(ResourceBundle bundle,
2019                                           LoadSession loadSession,
2020                                           Control control) {
2021         long ttl = control.getTimeToLive(loadSession.getName(),
2022                                          loadSession.getLocale());
2023         if (ttl >= 0) {
2024             // If any expiration time is specified, set the time to be
2025             // expired in the cache.
2026             long now = System.currentTimeMillis();
2027             bundle.loadTime = now;
2028             bundle.expirationTime = now + ttl;
2029         } else if (ttl >= Control.TTL_NO_EXPIRATION_CONTROL) {
2030             bundle.expirationTime = ttl;
2031         } else {
2032             throw new IllegalArgumentException("Invalid Control: TTL=" + ttl);
2033         }
2034     }
2035 
2036     /**
2037      * Removes all resource bundles from the cache that have been loaded
2038      * by the caller's module using the caller's class loader.
2039      *
2040      * @since 1.6
2041      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
2042      */
2043     @CallerSensitive
2044     public static final void clearCache() {
2045         Class<?> caller = Reflection.getCallerClass();
2046         clearCache(getLoader(caller), caller.getModule());
2047     }
2048 
2049     /**
2050      * Removes all resource bundles from the cache that have been loaded


2063 
2064     /**
2065      * Removes all resource bundles from the cache that have been loaded by the
2066      * given {@code module}.
2067      *
2068      * @param module the module
2069      * @throws NullPointerException
2070      *         if {@code module} is {@code null}
2071      * @throws SecurityException
2072      *         if the caller doesn't have the permission to
2073      *         {@linkplain Module#getClassLoader() get the class loader}
2074      *         of the given {@code module}
2075      * @since 9
2076      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
2077      */
2078     public static final void clearCache(Module module) {
2079         clearCache(module.getClassLoader(), module);
2080     }
2081 
2082     private static void clearCache(ClassLoader loader, Module module) {
2083         cache.sub(module).removeAll(loader);


2084     }
2085 
2086     /**
2087      * Gets an object for the given key from this resource bundle.
2088      * Returns null if this resource bundle does not contain an
2089      * object for the given key.
2090      *
2091      * @param key the key for the desired object
2092      * @exception NullPointerException if <code>key</code> is <code>null</code>
2093      * @return the object for the given key, or null
2094      */
2095     protected abstract Object handleGetObject(String key);
2096 
2097     /**
2098      * Returns an enumeration of the keys.
2099      *
2100      * @return an <code>Enumeration</code> of the keys contained in
2101      *         this <code>ResourceBundle</code> and its parent bundles.
2102      */
2103     public abstract Enumeration<String> getKeys();


< prev index next >