< prev index next >

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

Print this page




 362                 }
 363 
 364                 @Override
 365                 public ResourceBundle getParent(ResourceBundle bundle) {
 366                     return bundle.parent;
 367                 }
 368 
 369                 @Override
 370                 public void setLocale(ResourceBundle bundle, Locale locale) {
 371                     bundle.locale = locale;
 372                 }
 373 
 374                 @Override
 375                 public void setName(ResourceBundle bundle, String name) {
 376                     bundle.name = name;
 377                 }
 378 
 379                 @Override
 380                 public ResourceBundle getBundle(String baseName, Locale locale, Module module) {
 381                     // use the given module as the caller to bypass the access check
 382                     return getBundleImpl(module, module, getLoader(module),
 383                                          baseName, locale, Control.INSTANCE);
 384                 }
 385 
 386                 @Override
 387                 public ResourceBundle newResourceBundle(Class<? extends ResourceBundle> bundleClass) {
 388                     return ResourceBundleProviderHelper.newResourceBundle(bundleClass);
 389                 }
 390             });
 391     }
 392 
 393     /** constant indicating that no resource bundle exists */
 394     private static final ResourceBundle NONEXISTENT_BUNDLE = new ResourceBundle() {
 395             public Enumeration<String> getKeys() { return null; }
 396             protected Object handleGetObject(String key) { return null; }
 397             public String toString() { return "NONEXISTENT_BUNDLE"; }
 398         };
 399 
 400 
 401     /**
 402      * The cache is a map from cache keys (with bundle base name, locale, and


 549                                                    +this.getClass().getName()
 550                                                    +", key "+key,
 551                                                    this.getClass().getName(),
 552                                                    key);
 553             }
 554         }
 555         return obj;
 556     }
 557 
 558     /**
 559      * Returns the locale of this resource bundle. This method can be used after a
 560      * call to getBundle() to determine whether the resource bundle returned really
 561      * corresponds to the requested locale or is a fallback.
 562      *
 563      * @return the locale of this resource bundle
 564      */
 565     public Locale getLocale() {
 566         return locale;
 567     }
 568 
 569     /*
 570      * Automatic determination of the ClassLoader to be used to load
 571      * resources on behalf of the client.
 572      */
 573     private static ClassLoader getLoader(Class<?> caller) {
 574         ClassLoader cl = caller == null ? null : caller.getClassLoader();
 575         if (cl == null) {
 576             // When the caller's loader is the boot class loader, cl is null
 577             // here. In that case, ClassLoader.getSystemClassLoader() may
 578             // return the same class loader that the application is
 579             // using. We therefore use a wrapper ClassLoader to create a
 580             // separate scope for bundles loaded on behalf of the Java
 581             // runtime so that these bundles cannot be returned from the
 582             // cache to the application (5048280).
 583             cl = RBClassLoader.INSTANCE;
 584         }
 585         return cl;
 586     }
 587 
 588     private static ClassLoader getLoader(Module module) {
 589         PrivilegedAction<ClassLoader> pa = module::getClassLoader;
 590         return AccessController.doPrivileged(pa);
 591     }
 592 
 593     /**
 594      * A wrapper of ClassLoader.getSystemClassLoader().


 595      */
 596     private static class RBClassLoader extends ClassLoader {
 597         private static final RBClassLoader INSTANCE = AccessController.doPrivileged(
 598                     new PrivilegedAction<RBClassLoader>() {
 599                         public RBClassLoader run() {
 600                             return new RBClassLoader();
 601                         }
 602                     });
 603         private RBClassLoader() {
 604         }
 605         public Class<?> loadClass(String name) throws ClassNotFoundException {
 606             ClassLoader loader = ClassLoader.getSystemClassLoader();
 607             if (loader != null) {
 608                 return loader.loadClass(name);
 609             }
 610             return Class.forName(name);
 611         }
 612         public URL getResource(String name) {
 613             ClassLoader loader = ClassLoader.getSystemClassLoader();
 614             if (loader != null) {
 615                 return loader.getResource(name);
 616             }
 617             return ClassLoader.getSystemResource(name);
 618         }
 619         public InputStream getResourceAsStream(String name) {
 620             ClassLoader loader = ClassLoader.getSystemClassLoader();
 621             if (loader != null) {
 622                 return loader.getResourceAsStream(name);
 623             }
 624             return ClassLoader.getSystemResourceAsStream(name);
 625         }
 626     }
 627 
 628     /**
 629      * Sets the parent bundle of this bundle.
 630      * The parent bundle is searched by {@link #getObject getObject}
 631      * when this bundle does not contain a particular resource.
 632      *
 633      * @param parent this bundle's parent bundle.
 634      */
 635     protected void setParent(ResourceBundle parent) {
 636         assert parent != NONEXISTENT_BUNDLE;
 637         this.parent = parent;
 638     }
 639 
 640     /**
 641      * Key used for cached resource bundles.  The key checks the base
 642      * name, the locale, the class loader, and the caller module
 643      * to determine if the resource is a match to the requested one.
 644      * The loader may be null, but the base name, the locale and
 645      * module must have a non-null value.
 646      */
 647     private static class CacheKey implements Cloneable {
 648         // These four are the actual keys for lookup in Map.
 649         private String name;
 650         private Locale locale;
 651         private KeyElementReference<ClassLoader> loaderRef;
 652         private KeyElementReference<Module> moduleRef;
 653 



 654 
 655         // bundle format which is necessary for calling
 656         // Control.needsReload().
 657         private String format;
 658 
 659         // These time values are in CacheKey so that NONEXISTENT_BUNDLE
 660         // doesn't need to be cloned for caching.
 661 
 662         // The time when the bundle has been loaded
 663         private volatile long loadTime;
 664 
 665         // The time when the bundle expires in the cache, or either
 666         // Control.TTL_DONT_CACHE or Control.TTL_NO_EXPIRATION_CONTROL.
 667         private volatile long expirationTime;
 668 
 669         // Placeholder for an error report by a Throwable
 670         private Throwable cause;
 671 
 672         // Hash code value cache to avoid recalculating the hash code
 673         // of this instance.
 674         private int hashCodeCache;
 675 
 676         // ResourceBundleProviders for loading ResourceBundles
 677         private ServiceLoader<ResourceBundleProvider> providers;
 678         private boolean providersChecked;
 679 
 680         // Boolean.TRUE if the factory method caller provides a ResourceBundleProvier.
 681         private Boolean callerHasProvider;
 682 
 683         CacheKey(String baseName, Locale locale, ClassLoader loader, Module module) {
 684             Objects.requireNonNull(module);

 685 
 686             this.name = baseName;
 687             this.locale = locale;
 688             if (loader == null) {
 689                 this.loaderRef = null;
 690             } else {
 691                 this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this);
 692             }
 693             this.moduleRef = new KeyElementReference<>(module, referenceQueue, this);
 694             calculateHashCode();

 695         }
 696 
 697         String getName() {
 698             return name;
 699         }
 700 
 701         CacheKey setName(String baseName) {
 702             if (!this.name.equals(baseName)) {
 703                 this.name = baseName;
 704                 calculateHashCode();
 705             }
 706             return this;
 707         }
 708 
 709         Locale getLocale() {
 710             return locale;
 711         }
 712 
 713         CacheKey setLocale(Locale locale) {
 714             if (!this.locale.equals(locale)) {
 715                 this.locale = locale;
 716                 calculateHashCode();
 717             }
 718             return this;
 719         }
 720 
 721         ClassLoader getLoader() {
 722             return (loaderRef != null) ? loaderRef.get() : null;
 723         }
 724 
 725         Module getModule() {
 726             return moduleRef.get();
 727         }
 728 
 729         ServiceLoader<ResourceBundleProvider> getProviders() {
 730             if (!providersChecked) {
 731                 providers = getServiceLoader(getModule(), name);
 732                 providersChecked = true;
 733             }
 734             return providers;
 735         }
 736 
 737         boolean hasProviders() {
 738             return getProviders() != null;
 739         }
 740 
 741         boolean callerHasProvider() {
 742             return callerHasProvider == Boolean.TRUE;
 743         }
 744 
 745         @Override
 746         public boolean equals(Object other) {
 747             if (this == other) {
 748                 return true;
 749             }
 750             try {
 751                 final CacheKey otherEntry = (CacheKey)other;
 752                 //quick check to see if they are not equal
 753                 if (hashCodeCache != otherEntry.hashCodeCache) {
 754                     return false;
 755                 }
 756                 //are the names the same?
 757                 if (!name.equals(otherEntry.name)) {
 758                     return false;
 759                 }
 760                 // are the locales the same?
 761                 if (!locale.equals(otherEntry.locale)) {
 762                     return false;
 763                 }
 764                 //are refs (both non-null) or (both null)?
 765                 if (loaderRef == null) {
 766                     return otherEntry.loaderRef == null;
 767                 }
 768                 ClassLoader loader = getLoader();
 769                 Module module = getModule();
 770                 return (otherEntry.loaderRef != null)
 771                         // with a null reference we can no longer find
 772                         // out which class loader or module was referenced; so
 773                         // treat it as unequal
 774                         && (loader != null)
 775                         && (loader == otherEntry.getLoader())
 776                         && (module != null)
 777                         && (module.equals(otherEntry.getModule()));
 778             } catch (NullPointerException | ClassCastException e) {
 779             }
 780             return false;
 781         }
 782 
 783         @Override
 784         public int hashCode() {
 785             return hashCodeCache;
 786         }
 787 
 788         private void calculateHashCode() {
 789             hashCodeCache = name.hashCode() << 3;
 790             hashCodeCache ^= locale.hashCode();
 791             ClassLoader loader = getLoader();
 792             if (loader != null) {
 793                 hashCodeCache ^= loader.hashCode();
 794             }
 795             Module module = getModule();
 796             if (module != null) {
 797                 hashCodeCache ^= module.hashCode();
 798             }
 799         }
 800 
 801         @Override
 802         public Object clone() {
 803             try {
 804                 CacheKey clone = (CacheKey) super.clone();
 805                 if (loaderRef != null) {
 806                     clone.loaderRef = new KeyElementReference<>(getLoader(),
 807                                                                 referenceQueue, clone);
 808                 }
 809                 clone.moduleRef = new KeyElementReference<>(getModule(),
 810                                                             referenceQueue, clone);









 811                 // Clear the reference to ResourceBundleProviders and the flag
 812                 clone.providers = null;
 813                 clone.providersChecked = false;
 814                 // Clear the reference to a Throwable
 815                 clone.cause = null;
 816                 // Clear callerHasProvider
 817                 clone.callerHasProvider = null;
 818                 return clone;
 819             } catch (CloneNotSupportedException e) {
 820                 //this should never happen
 821                 throw new InternalError(e);
 822             }
 823         }
 824 
 825         String getFormat() {
 826             return format;
 827         }
 828 
 829         void setFormat(String format) {
 830             this.format = format;


 839                 if (this.cause instanceof ClassNotFoundException) {
 840                     this.cause = cause;
 841                 }
 842             }
 843         }
 844 
 845         private Throwable getCause() {
 846             return cause;
 847         }
 848 
 849         @Override
 850         public String toString() {
 851             String l = locale.toString();
 852             if (l.length() == 0) {
 853                 if (locale.getVariant().length() != 0) {
 854                     l = "__" + locale.getVariant();
 855                 } else {
 856                     l = "\"\"";
 857                 }
 858             }
 859             return "CacheKey[" + name + ", lc=" + l + ", ldr=" + getLoader()
 860                 + "(format=" + format + ")]";




 861         }
 862     }
 863 
 864     /**
 865      * The common interface to get a CacheKey in LoaderReference and
 866      * BundleReference.
 867      */
 868     private static interface CacheKeyReference {
 869         public CacheKey getCacheKey();
 870     }
 871 
 872     /**
 873      * References to a CacheKey element as a WeakReference so that it can be
 874      * garbage collected when nobody else is using it.
 875      */
 876     private static class KeyElementReference<T> extends WeakReference<T>
 877                                                 implements CacheKeyReference {
 878         private final CacheKey cacheKey;
 879 
 880         KeyElementReference(T referent, ReferenceQueue<Object> q, CacheKey key) {


1577                 Control control = provider.getControl(baseName);
1578                 if (control != null) {
1579                     return control;
1580                 }
1581             }
1582         }
1583         return Control.INSTANCE;
1584     }
1585 
1586     private static void checkNamedModule(Class<?> caller) {
1587         if (caller.getModule().isNamed()) {
1588             throw new UnsupportedOperationException(
1589                     "ResourceBundle.Control not supported in named modules");
1590         }
1591     }
1592 
1593     private static ResourceBundle getBundleImpl(String baseName,
1594                                                 Locale locale,
1595                                                 Class<?> caller,
1596                                                 Control control) {
1597         return getBundleImpl(baseName, locale, caller, getLoader(caller), control);
1598     }
1599 
1600     /**
1601      * This method will find resource bundles using the legacy mechanism
1602      * if the caller is unnamed module or the given class loader is
1603      * not the class loader of the caller module getting the resource
1604      * bundle, i.e. find the class that is visible to the class loader
1605      * and properties from unnamed module.
1606      *
1607      * The module-aware resource bundle lookup mechanism will load
1608      * the service providers using the service loader mechanism
1609      * as well as properties local in the caller module.
1610      */
1611     private static ResourceBundle getBundleImpl(String baseName,
1612                                                 Locale locale,
1613                                                 Class<?> caller,
1614                                                 ClassLoader loader,
1615                                                 Control control) {
1616         if (caller != null && caller.getModule().isNamed()) {
1617             Module module = caller.getModule();
1618             ClassLoader ml = getLoader(module);
1619             // get resource bundles for a named module only
1620             // if loader is the module's class loader
1621             if (loader == ml || (ml == null && loader == RBClassLoader.INSTANCE)) {
1622                 return getBundleImpl(module, module, loader, baseName, locale, control);
1623             }





1624         }
1625         // find resource bundles from unnamed module
1626         Module unnamedModule = loader != null
1627             ? loader.getUnnamedModule()
1628             : ClassLoader.getSystemClassLoader().getUnnamedModule();
1629 
1630         if (caller == null) {
1631             throw new InternalError("null caller");


1632         }
1633 
1634         Module callerModule = caller.getModule();
1635         return getBundleImpl(callerModule, unnamedModule, loader, baseName, locale, control);

1636     }
1637 
1638     private static ResourceBundle getBundleFromModule(Class<?> caller,
1639                                                       Module module,
1640                                                       String baseName,
1641                                                       Locale locale,
1642                                                       Control control) {
1643         Objects.requireNonNull(module);
1644         Module callerModule = caller.getModule();
1645         if (callerModule != module) {
1646             SecurityManager sm = System.getSecurityManager();
1647             if (sm != null) {
1648                 sm.checkPermission(GET_CLASSLOADER_PERMISSION);
1649             }
1650         }
1651         return getBundleImpl(callerModule, module, getLoader(module), baseName, locale, control);
1652     }
1653 
1654     private static ResourceBundle getBundleImpl(Module callerModule,
1655                                                 Module module,
1656                                                 ClassLoader loader,
1657                                                 String baseName,
1658                                                 Locale locale,
1659                                                 Control control) {
1660         if (locale == null || control == null) {
1661             throw new NullPointerException();
1662         }
1663 
1664         // We create a CacheKey here for use by this call. The base name
1665         // loader, and module will never change during the bundle loading
1666         // process. We have to make sure that the locale is set before
1667         // using it as a cache key.
1668         CacheKey cacheKey = new CacheKey(baseName, locale, loader, module);
1669         ResourceBundle bundle = null;
1670 
1671         // Quick lookup of the cache.
1672         BundleReference bundleRef = cacheList.get(cacheKey);
1673         if (bundleRef != null) {
1674             bundle = bundleRef.get();
1675             bundleRef = null;
1676         }
1677 
1678         // If this bundle and all of its parents are valid (not expired),
1679         // then return this bundle. If any of the bundles is expired, we
1680         // don't call control.needsReload here but instead drop into the
1681         // complete loading process below.
1682         if (isValidBundle(bundle) && hasValidParentChain(bundle)) {
1683             return bundle;
1684         }
1685 
1686         // No valid bundle was found in the cache, so we need to load the
1687         // resource bundle and its parents.
1688 


2000                             cacheKey.callerHasProvider = Boolean.FALSE;
2001                         }
2002                         return null;
2003                     }
2004                 });
2005 
2006     }
2007 
2008     /*
2009      * Legacy mechanism to load resource bundles
2010      */
2011     private static ResourceBundle loadBundle(CacheKey cacheKey,
2012                                              List<String> formats,
2013                                              Control control,
2014                                              boolean reload) {
2015 
2016         // Here we actually load the bundle in the order of formats
2017         // specified by the getFormats() value.
2018         Locale targetLocale = cacheKey.getLocale();
2019 








2020         ResourceBundle bundle = null;
2021         for (String format : formats) {
2022             try {
2023                 // ResourceBundle.Control.newBundle may be overridden
2024                 bundle = control.newBundle(cacheKey.getName(), targetLocale, format,
2025                                            cacheKey.getLoader(), reload);
2026             } catch (LinkageError | Exception error) {
2027                 // We need to handle the LinkageError case due to
2028                 // inconsistent case-sensitivity in ClassLoader.
2029                 // See 6572242 for details.
2030                 cacheKey.setCause(error);
2031             }
2032             if (bundle != null) {
2033                 // Set the format in the cache key so that it can be
2034                 // used when calling needsReload later.
2035                 cacheKey.setFormat(format);
2036                 bundle.name = cacheKey.getName();
2037                 bundle.locale = targetLocale;
2038                 // Bundle provider might reuse instances. So we should make
2039                 // sure to clear the expired flag here.
2040                 bundle.expired = false;
2041                 break;
2042             }
2043         }
2044 
2045         return bundle;


2147         if (p != null && p.expired) {
2148             assert bundle != NONEXISTENT_BUNDLE;
2149             bundle.expired = true;
2150             bundle.cacheKey = null;
2151             cacheList.remove(cacheKey, bundleRef);
2152             bundle = null;
2153         } else {
2154             CacheKey key = bundleRef.getCacheKey();
2155             long expirationTime = key.expirationTime;
2156             if (!bundle.expired && expirationTime >= 0 &&
2157                 expirationTime <= System.currentTimeMillis()) {
2158                 // its TTL period has expired.
2159                 if (bundle != NONEXISTENT_BUNDLE) {
2160                     // Synchronize here to call needsReload to avoid
2161                     // redundant concurrent calls for the same bundle.
2162                     synchronized (bundle) {
2163                         expirationTime = key.expirationTime;
2164                         if (!bundle.expired && expirationTime >= 0 &&
2165                             expirationTime <= System.currentTimeMillis()) {
2166                             try {
2167                                 bundle.expired = control.needsReload(key.getName(),



2168                                                                      key.getLocale(),
2169                                                                      key.getFormat(),
2170                                                                      key.getLoader(),
2171                                                                      bundle,
2172                                                                      key.loadTime);
2173                             } catch (Exception e) {
2174                                 cacheKey.setCause(e);
2175                             }
2176                             if (bundle.expired) {
2177                                 // If the bundle needs to be reloaded, then
2178                                 // remove the bundle from the cache, but
2179                                 // return the bundle with the expired flag
2180                                 // on.
2181                                 bundle.cacheKey = null;
2182                                 cacheList.remove(cacheKey, bundleRef);
2183                             } else {
2184                                 // Update the expiration control info. and reuse
2185                                 // the same bundle instance
2186                                 setExpirationTime(key, control);
2187                             }
2188                         }
2189                     }
2190                 } else {


2248             long now = System.currentTimeMillis();
2249             cacheKey.loadTime = now;
2250             cacheKey.expirationTime = now + ttl;
2251         } else if (ttl >= Control.TTL_NO_EXPIRATION_CONTROL) {
2252             cacheKey.expirationTime = ttl;
2253         } else {
2254             throw new IllegalArgumentException("Invalid Control: TTL=" + ttl);
2255         }
2256     }
2257 
2258     /**
2259      * Removes all resource bundles from the cache that have been loaded
2260      * by the caller's module using the caller's class loader.
2261      *
2262      * @since 1.6
2263      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
2264      */
2265     @CallerSensitive
2266     public static final void clearCache() {
2267         Class<?> caller = Reflection.getCallerClass();
2268         clearCache(getLoader(caller), caller.getModule());
2269     }
2270 
2271     /**
2272      * Removes all resource bundles from the cache that have been loaded
2273      * by the caller's module using the given class loader.
2274      *
2275      * @param loader the class loader
2276      * @exception NullPointerException if <code>loader</code> is null
2277      * @since 1.6
2278      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
2279      */
2280     @CallerSensitive
2281     public static final void clearCache(ClassLoader loader) {
2282         Objects.requireNonNull(loader);
2283         clearCache(loader, Reflection.getCallerClass().getModule());

2284     }
2285 
2286     /**
2287      * Removes all resource bundles from the cache that have been loaded by the
2288      * given {@code module}.
2289      *
2290      * @param module the module
2291      * @throws NullPointerException
2292      *         if {@code module} is {@code null}
2293      * @throws SecurityException
2294      *         if the caller doesn't have the permission to
2295      *         {@linkplain Module#getClassLoader() get the class loader}
2296      *         of the given {@code module}
2297      * @since 9
2298      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
2299      */
2300     public static final void clearCache(Module module) {
2301         clearCache(module.getClassLoader(), module);

2302     }
2303 
2304     private static void clearCache(ClassLoader loader, Module module) {
2305         Set<CacheKey> set = cacheList.keySet();
2306         set.stream().filter((key) -> (key.getLoader() == loader && key.getModule() == module))
2307                 .forEach(set::remove);

2308     }
2309 
2310     /**
2311      * Gets an object for the given key from this resource bundle.
2312      * Returns null if this resource bundle does not contain an
2313      * object for the given key.
2314      *
2315      * @param key the key for the desired object
2316      * @exception NullPointerException if <code>key</code> is <code>null</code>
2317      * @return the object for the given key, or null
2318      */
2319     protected abstract Object handleGetObject(String key);
2320 
2321     /**
2322      * Returns an enumeration of the keys.
2323      *
2324      * @return an <code>Enumeration</code> of the keys contained in
2325      *         this <code>ResourceBundle</code> and its parent bundles.
2326      */
2327     public abstract Enumeration<String> getKeys();




 362                 }
 363 
 364                 @Override
 365                 public ResourceBundle getParent(ResourceBundle bundle) {
 366                     return bundle.parent;
 367                 }
 368 
 369                 @Override
 370                 public void setLocale(ResourceBundle bundle, Locale locale) {
 371                     bundle.locale = locale;
 372                 }
 373 
 374                 @Override
 375                 public void setName(ResourceBundle bundle, String name) {
 376                     bundle.name = name;
 377                 }
 378 
 379                 @Override
 380                 public ResourceBundle getBundle(String baseName, Locale locale, Module module) {
 381                     // use the given module as the caller to bypass the access check
 382                     return getBundleImpl(module, module,
 383                                          baseName, locale, Control.INSTANCE);
 384                 }
 385 
 386                 @Override
 387                 public ResourceBundle newResourceBundle(Class<? extends ResourceBundle> bundleClass) {
 388                     return ResourceBundleProviderHelper.newResourceBundle(bundleClass);
 389                 }
 390             });
 391     }
 392 
 393     /** constant indicating that no resource bundle exists */
 394     private static final ResourceBundle NONEXISTENT_BUNDLE = new ResourceBundle() {
 395             public Enumeration<String> getKeys() { return null; }
 396             protected Object handleGetObject(String key) { return null; }
 397             public String toString() { return "NONEXISTENT_BUNDLE"; }
 398         };
 399 
 400 
 401     /**
 402      * The cache is a map from cache keys (with bundle base name, locale, and


 549                                                    +this.getClass().getName()
 550                                                    +", key "+key,
 551                                                    this.getClass().getName(),
 552                                                    key);
 553             }
 554         }
 555         return obj;
 556     }
 557 
 558     /**
 559      * Returns the locale of this resource bundle. This method can be used after a
 560      * call to getBundle() to determine whether the resource bundle returned really
 561      * corresponds to the requested locale or is a fallback.
 562      *
 563      * @return the locale of this resource bundle
 564      */
 565     public Locale getLocale() {
 566         return locale;
 567     }
 568 



















 569     private static ClassLoader getLoader(Module module) {
 570         PrivilegedAction<ClassLoader> pa = module::getClassLoader;
 571         return AccessController.doPrivileged(pa);
 572     }
 573 
 574     /**
 575      * @param module a non-null-screened module form the {@link CacheKey#getModule()}.
 576      * @return the ClassLoader to use in {@link Control#needsReload}
 577      *         and {@link Control#newBundle}
 578      */
 579     private static ClassLoader getLoaderForControl(Module module) {
 580         ClassLoader loader = getLoader(module);
 581         return loader == null ? ClassLoader.getSystemClassLoader() : loader;



























 582     }
 583 
 584     /**
 585      * Sets the parent bundle of this bundle.
 586      * The parent bundle is searched by {@link #getObject getObject}
 587      * when this bundle does not contain a particular resource.
 588      *
 589      * @param parent this bundle's parent bundle.
 590      */
 591     protected void setParent(ResourceBundle parent) {
 592         assert parent != NONEXISTENT_BUNDLE;
 593         this.parent = parent;
 594     }
 595 
 596     /**
 597      * Key used for cached resource bundles.  The key checks the base
 598      * name, the locale, the bundle module, and the caller module
 599      * to determine if the resource is a match to the requested one.
 600      * The base name, the locale and both modules must have a non-null value.

 601      */
 602     private static class CacheKey implements Cloneable {
 603         // These four are the actual keys for lookup in Map.
 604         private String name;
 605         private Locale locale;

 606         private KeyElementReference<Module> moduleRef;
 607         private KeyElementReference<Module> callerRef;
 608         // this is the part of hashCode that pertains to module and callerModule
 609         // which can be GCed..
 610         private int modulesHash;
 611 
 612         // bundle format which is necessary for calling
 613         // Control.needsReload().
 614         private String format;
 615 
 616         // These time values are in CacheKey so that NONEXISTENT_BUNDLE
 617         // doesn't need to be cloned for caching.
 618 
 619         // The time when the bundle has been loaded
 620         private volatile long loadTime;
 621 
 622         // The time when the bundle expires in the cache, or either
 623         // Control.TTL_DONT_CACHE or Control.TTL_NO_EXPIRATION_CONTROL.
 624         private volatile long expirationTime;
 625 
 626         // Placeholder for an error report by a Throwable
 627         private Throwable cause;
 628 




 629         // ResourceBundleProviders for loading ResourceBundles
 630         private ServiceLoader<ResourceBundleProvider> providers;
 631         private boolean providersChecked;
 632 
 633         // Boolean.TRUE if the factory method caller provides a ResourceBundleProvier.
 634         private Boolean callerHasProvider;
 635 
 636         CacheKey(String baseName, Locale locale, Module module, Module caller) {
 637             Objects.requireNonNull(module);
 638             Objects.requireNonNull(caller);
 639 
 640             this.name = baseName;
 641             this.locale = locale;





 642             this.moduleRef = new KeyElementReference<>(module, referenceQueue, this);
 643             this.callerRef = new KeyElementReference<>(caller, referenceQueue, this);
 644             this.modulesHash = module.hashCode() ^ caller.hashCode();
 645         }
 646 
 647         String getName() {
 648             return name;
 649         }
 650 
 651         CacheKey setName(String baseName) {

 652             this.name = baseName;


 653             return this;
 654         }
 655 
 656         Locale getLocale() {
 657             return locale;
 658         }
 659 
 660         CacheKey setLocale(Locale locale) {

 661             this.locale = locale;


 662             return this;
 663         }
 664 
 665         Module getModule() {
 666             return moduleRef == null ? null : moduleRef.get();
 667         }
 668 
 669         Module getCallerModule() {
 670             return callerRef == null ? null : callerRef.get();
 671         }
 672 
 673         ServiceLoader<ResourceBundleProvider> getProviders() {
 674             if (!providersChecked) {
 675                 providers = getServiceLoader(getModule(), name);
 676                 providersChecked = true;
 677             }
 678             return providers;
 679         }
 680 
 681         boolean hasProviders() {
 682             return getProviders() != null;
 683         }
 684 
 685         boolean callerHasProvider() {
 686             return callerHasProvider == Boolean.TRUE;
 687         }
 688 
 689         @Override
 690         public boolean equals(Object other) {
 691             if (this == other) {
 692                 return true;
 693             }
 694             try {
 695                 final CacheKey otherEntry = (CacheKey)other;
 696                 //quick check to see if they are not equal
 697                 if (modulesHash != otherEntry.modulesHash) {
 698                     return false;
 699                 }
 700                 //are the names the same?
 701                 if (!name.equals(otherEntry.name)) {
 702                     return false;
 703                 }
 704                 // are the locales the same?
 705                 if (!locale.equals(otherEntry.locale)) {
 706                     return false;
 707                 }
 708                 // are modules and callerModules the same and non-null?




 709                 Module module = getModule();
 710                 Module caller = getCallerModule();
 711                 return ((module != null) && (module.equals(otherEntry.getModule())) &&
 712                         (caller != null) && (caller.equals(otherEntry.getCallerModule())));





 713             } catch (NullPointerException | ClassCastException e) {
 714             }
 715             return false;
 716         }
 717 
 718         @Override
 719         public int hashCode() {
 720             return (name.hashCode() << 3) ^ locale.hashCode() ^ modulesHash;













 721         }
 722 
 723         @Override
 724         public Object clone() {
 725             try {
 726                 CacheKey clone = (CacheKey) super.clone();
 727 
 728                 Module module = getModule();
 729                 clone.moduleRef =
 730                     module == null
 731                     ? null // don't ever create a Reference for a null referent
 732                            // because it will never be enqueued and consequently
 733                            // its CacheKey will never be removed from cacheList...
 734                     : new KeyElementReference<>(module, referenceQueue, clone);
 735 
 736                 Module caller = getCallerModule();
 737                 clone.callerRef =
 738                     caller == null
 739                     ? null
 740                     : new KeyElementReference<>(caller, referenceQueue, clone);
 741 
 742                 // Clear the reference to ResourceBundleProviders and the flag
 743                 clone.providers = null;
 744                 clone.providersChecked = false;
 745                 // Clear the reference to a Throwable
 746                 clone.cause = null;
 747                 // Clear callerHasProvider
 748                 clone.callerHasProvider = null;
 749                 return clone;
 750             } catch (CloneNotSupportedException e) {
 751                 //this should never happen
 752                 throw new InternalError(e);
 753             }
 754         }
 755 
 756         String getFormat() {
 757             return format;
 758         }
 759 
 760         void setFormat(String format) {
 761             this.format = format;


 770                 if (this.cause instanceof ClassNotFoundException) {
 771                     this.cause = cause;
 772                 }
 773             }
 774         }
 775 
 776         private Throwable getCause() {
 777             return cause;
 778         }
 779 
 780         @Override
 781         public String toString() {
 782             String l = locale.toString();
 783             if (l.length() == 0) {
 784                 if (locale.getVariant().length() != 0) {
 785                     l = "__" + locale.getVariant();
 786                 } else {
 787                     l = "\"\"";
 788                 }
 789             }
 790             return "CacheKey[" + name +
 791                    ", locale=" + l +
 792                    ", module=" + getModule() +
 793                    ", callerModule=" + getCallerModule() +
 794                    ", format=" + format +
 795                    "]";
 796         }
 797     }
 798 
 799     /**
 800      * The common interface to get a CacheKey in LoaderReference and
 801      * BundleReference.
 802      */
 803     private static interface CacheKeyReference {
 804         public CacheKey getCacheKey();
 805     }
 806 
 807     /**
 808      * References to a CacheKey element as a WeakReference so that it can be
 809      * garbage collected when nobody else is using it.
 810      */
 811     private static class KeyElementReference<T> extends WeakReference<T>
 812                                                 implements CacheKeyReference {
 813         private final CacheKey cacheKey;
 814 
 815         KeyElementReference(T referent, ReferenceQueue<Object> q, CacheKey key) {


1512                 Control control = provider.getControl(baseName);
1513                 if (control != null) {
1514                     return control;
1515                 }
1516             }
1517         }
1518         return Control.INSTANCE;
1519     }
1520 
1521     private static void checkNamedModule(Class<?> caller) {
1522         if (caller.getModule().isNamed()) {
1523             throw new UnsupportedOperationException(
1524                     "ResourceBundle.Control not supported in named modules");
1525         }
1526     }
1527 
1528     private static ResourceBundle getBundleImpl(String baseName,
1529                                                 Locale locale,
1530                                                 Class<?> caller,
1531                                                 Control control) {
1532         return getBundleImpl(baseName, locale, caller, caller.getClassLoader(), control);
1533     }
1534 
1535     /**
1536      * This method will find resource bundles using the legacy mechanism
1537      * if the caller is unnamed module or the given class loader is
1538      * not the class loader of the caller module getting the resource
1539      * bundle, i.e. find the class that is visible to the class loader
1540      * and properties from unnamed module.
1541      *
1542      * The module-aware resource bundle lookup mechanism will load
1543      * the service providers using the service loader mechanism
1544      * as well as properties local in the caller module.
1545      */
1546     private static ResourceBundle getBundleImpl(String baseName,
1547                                                 Locale locale,
1548                                                 Class<?> caller,
1549                                                 ClassLoader loader,
1550                                                 Control control) {
1551         if (caller == null) {
1552             throw new InternalError("null caller");





1553         }
1554         Module callerModule = caller.getModule();
1555 
1556         // get resource bundles for a named module only if loader is the module's class loader
1557         if (callerModule.isNamed() && loader == getLoader(callerModule)) {
1558             return getBundleImpl(callerModule, callerModule, baseName, locale, control);
1559         }




1560 
1561         // there's no code in unnamed module of bootstrap class loader so loader
1562         // must be non-null (non-bootstrap) if the caller is from unnamed module
1563         if (loader == null) {
1564             throw new InternalError("null loader");
1565         }
1566 
1567         // find resource bundles from unnamed module of given class loader
1568         Module unnamedModule = loader.getUnnamedModule();
1569         return getBundleImpl(callerModule, unnamedModule, baseName, locale, control);
1570     }
1571 
1572     private static ResourceBundle getBundleFromModule(Class<?> caller,
1573                                                       Module module,
1574                                                       String baseName,
1575                                                       Locale locale,
1576                                                       Control control) {
1577         Objects.requireNonNull(module);
1578         Module callerModule = caller.getModule();
1579         if (callerModule != module) {
1580             SecurityManager sm = System.getSecurityManager();
1581             if (sm != null) {
1582                 sm.checkPermission(GET_CLASSLOADER_PERMISSION);
1583             }
1584         }
1585         return getBundleImpl(callerModule, module, baseName, locale, control);
1586     }
1587 
1588     private static ResourceBundle getBundleImpl(Module callerModule,
1589                                                 Module module,

1590                                                 String baseName,
1591                                                 Locale locale,
1592                                                 Control control) {
1593         if (locale == null || control == null) {
1594             throw new NullPointerException();
1595         }
1596 
1597         // We create a CacheKey here for use by this call. The base name
1598         // loader, and module will never change during the bundle loading
1599         // process. We have to make sure that the locale is set before
1600         // using it as a cache key.
1601         CacheKey cacheKey = new CacheKey(baseName, locale, module, callerModule);
1602         ResourceBundle bundle = null;
1603 
1604         // Quick lookup of the cache.
1605         BundleReference bundleRef = cacheList.get(cacheKey);
1606         if (bundleRef != null) {
1607             bundle = bundleRef.get();
1608             bundleRef = null;
1609         }
1610 
1611         // If this bundle and all of its parents are valid (not expired),
1612         // then return this bundle. If any of the bundles is expired, we
1613         // don't call control.needsReload here but instead drop into the
1614         // complete loading process below.
1615         if (isValidBundle(bundle) && hasValidParentChain(bundle)) {
1616             return bundle;
1617         }
1618 
1619         // No valid bundle was found in the cache, so we need to load the
1620         // resource bundle and its parents.
1621 


1933                             cacheKey.callerHasProvider = Boolean.FALSE;
1934                         }
1935                         return null;
1936                     }
1937                 });
1938 
1939     }
1940 
1941     /*
1942      * Legacy mechanism to load resource bundles
1943      */
1944     private static ResourceBundle loadBundle(CacheKey cacheKey,
1945                                              List<String> formats,
1946                                              Control control,
1947                                              boolean reload) {
1948 
1949         // Here we actually load the bundle in the order of formats
1950         // specified by the getFormats() value.
1951         Locale targetLocale = cacheKey.getLocale();
1952 
1953         Module module = cacheKey.getModule();
1954         if (module == null) {
1955             // should not happen
1956             throw new InternalError(
1957                 "Module for cache key: " + cacheKey + " has been GCed.");
1958         }
1959         ClassLoader loader = getLoaderForControl(module);
1960 
1961         ResourceBundle bundle = null;
1962         for (String format : formats) {
1963             try {
1964                 // ResourceBundle.Control.newBundle may be overridden
1965                 bundle = control.newBundle(cacheKey.getName(), targetLocale, format,
1966                                            loader, reload);
1967             } catch (LinkageError | Exception error) {
1968                 // We need to handle the LinkageError case due to
1969                 // inconsistent case-sensitivity in ClassLoader.
1970                 // See 6572242 for details.
1971                 cacheKey.setCause(error);
1972             }
1973             if (bundle != null) {
1974                 // Set the format in the cache key so that it can be
1975                 // used when calling needsReload later.
1976                 cacheKey.setFormat(format);
1977                 bundle.name = cacheKey.getName();
1978                 bundle.locale = targetLocale;
1979                 // Bundle provider might reuse instances. So we should make
1980                 // sure to clear the expired flag here.
1981                 bundle.expired = false;
1982                 break;
1983             }
1984         }
1985 
1986         return bundle;


2088         if (p != null && p.expired) {
2089             assert bundle != NONEXISTENT_BUNDLE;
2090             bundle.expired = true;
2091             bundle.cacheKey = null;
2092             cacheList.remove(cacheKey, bundleRef);
2093             bundle = null;
2094         } else {
2095             CacheKey key = bundleRef.getCacheKey();
2096             long expirationTime = key.expirationTime;
2097             if (!bundle.expired && expirationTime >= 0 &&
2098                 expirationTime <= System.currentTimeMillis()) {
2099                 // its TTL period has expired.
2100                 if (bundle != NONEXISTENT_BUNDLE) {
2101                     // Synchronize here to call needsReload to avoid
2102                     // redundant concurrent calls for the same bundle.
2103                     synchronized (bundle) {
2104                         expirationTime = key.expirationTime;
2105                         if (!bundle.expired && expirationTime >= 0 &&
2106                             expirationTime <= System.currentTimeMillis()) {
2107                             try {
2108                                 Module module = cacheKey.getModule();
2109                                 bundle.expired =
2110                                     module == null || // already GCed
2111                                     control.needsReload(key.getName(),
2112                                                         key.getLocale(),
2113                                                         key.getFormat(),
2114                                                         getLoaderForControl(module),
2115                                                         bundle,
2116                                                         key.loadTime);
2117                             } catch (Exception e) {
2118                                 cacheKey.setCause(e);
2119                             }
2120                             if (bundle.expired) {
2121                                 // If the bundle needs to be reloaded, then
2122                                 // remove the bundle from the cache, but
2123                                 // return the bundle with the expired flag
2124                                 // on.
2125                                 bundle.cacheKey = null;
2126                                 cacheList.remove(cacheKey, bundleRef);
2127                             } else {
2128                                 // Update the expiration control info. and reuse
2129                                 // the same bundle instance
2130                                 setExpirationTime(key, control);
2131                             }
2132                         }
2133                     }
2134                 } else {


2192             long now = System.currentTimeMillis();
2193             cacheKey.loadTime = now;
2194             cacheKey.expirationTime = now + ttl;
2195         } else if (ttl >= Control.TTL_NO_EXPIRATION_CONTROL) {
2196             cacheKey.expirationTime = ttl;
2197         } else {
2198             throw new IllegalArgumentException("Invalid Control: TTL=" + ttl);
2199         }
2200     }
2201 
2202     /**
2203      * Removes all resource bundles from the cache that have been loaded
2204      * by the caller's module using the caller's class loader.
2205      *
2206      * @since 1.6
2207      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
2208      */
2209     @CallerSensitive
2210     public static final void clearCache() {
2211         Class<?> caller = Reflection.getCallerClass();
2212         clearCacheImpl(caller.getModule(), caller.getClassLoader());
2213     }
2214 
2215     /**
2216      * Removes all resource bundles from the cache that have been loaded
2217      * by the caller's module using the given class loader.
2218      *
2219      * @param loader the class loader
2220      * @exception NullPointerException if <code>loader</code> is null
2221      * @since 1.6
2222      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
2223      */
2224     @CallerSensitive
2225     public static final void clearCache(ClassLoader loader) {
2226         Objects.requireNonNull(loader);
2227         Class<?> caller = Reflection.getCallerClass();
2228         clearCacheImpl(caller.getModule(), loader);
2229     }
2230 
2231     /**
2232      * Removes all resource bundles from the cache that have been loaded by the
2233      * given {@code module}.
2234      *
2235      * @param module the module
2236      * @throws NullPointerException
2237      *         if {@code module} is {@code null}
2238      * @throws SecurityException
2239      *         if the caller doesn't have the permission to
2240      *         {@linkplain Module#getClassLoader() get the class loader}
2241      *         of the given {@code module}
2242      * @since 9
2243      * @see ResourceBundle.Control#getTimeToLive(String,Locale)
2244      */
2245     public static final void clearCache(Module module) {
2246         Objects.requireNonNull(module);
2247         clearCacheImpl(module, module.getClassLoader());
2248     }
2249 
2250     private static void clearCacheImpl(Module callerModule, ClassLoader loader) {
2251         cacheList.keySet().removeIf(
2252             key -> key.getCallerModule() == callerModule &&
2253                    getLoader(key.getModule()) == loader
2254         );
2255     }
2256 
2257     /**
2258      * Gets an object for the given key from this resource bundle.
2259      * Returns null if this resource bundle does not contain an
2260      * object for the given key.
2261      *
2262      * @param key the key for the desired object
2263      * @exception NullPointerException if <code>key</code> is <code>null</code>
2264      * @return the object for the given key, or null
2265      */
2266     protected abstract Object handleGetObject(String key);
2267 
2268     /**
2269      * Returns an enumeration of the keys.
2270      *
2271      * @return an <code>Enumeration</code> of the keys contained in
2272      *         this <code>ResourceBundle</code> and its parent bundles.
2273      */
2274     public abstract Enumeration<String> getKeys();


< prev index next >