8038436: Re-examine the mechanism to determine available localedata and cldrdata
authornaoto
Tue, 02 Sep 2014 14:11:38 -0700
changeset 26360 697f70835528
parent 26359 50d45d375866
child 26361 869d2905615d
8038436: Re-examine the mechanism to determine available localedata and cldrdata Reviewed-by: alanb, mchung, okutsu
jdk/make/CreateJars.gmk
jdk/make/gensrc/GensrcLocaleDataMetaInfo.gmk
jdk/make/src/classes/build/tools/cldrconverter/CLDRConverter.java
jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java
jdk/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java
jdk/src/java.base/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java
jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java
jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo-XLocales.java.template
jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo.java
jdk/src/java.base/share/classes/sun/util/resources/LocaleData.java
jdk/src/jdk.localedata/META-INF/cldrdata-services/sun.util.locale.provider.LocaleDataMetaInfo
jdk/src/jdk.localedata/META-INF/localedata-services/sun.util.locale.provider.LocaleDataMetaInfo
jdk/test/sun/util/locale/provider/Bug8038436.java
--- a/jdk/make/CreateJars.gmk	Tue Sep 02 11:21:29 2014 -0700
+++ b/jdk/make/CreateJars.gmk	Tue Sep 02 14:11:38 2014 -0700
@@ -76,13 +76,24 @@
     iw ja ko lt lv mk ms mt nl no pl pt ro ru sk sl sq sr sv \
     th tr uk vi zh
 
-LOCALEDATA_INCLUDES := $(addprefix sun/text/resources/, $(LOCALEDATA_INCLUDE_LOCALES)) \
+LOCALEDATA_INCLUDES := sun/util/resources/provider/NonEnLocaleDataMetaInfo.class
+LOCALEDATA_INCLUDES += $(addprefix sun/text/resources/, $(LOCALEDATA_INCLUDE_LOCALES)) \
     $(addprefix sun/util/resources/, $(LOCALEDATA_INCLUDE_LOCALES))
 
-$(eval $(call SetupArchive,BUILD_LOCALEDATA_JAR, , \
-    SRCS := $(JDK_OUTPUTDIR)/modules/jdk.localedata, \
+LOCALEDATA_SERVICES_DIR := $(IMAGES_OUTPUTDIR)/localemetainfo
+
+LOCALEDATA_METAINF_SERVICES := $(LOCALEDATA_SERVICES_DIR)/META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo
+
+$(LOCALEDATA_METAINF_SERVICES): $(JDK_TOPDIR)/src/jdk.localedata/META-INF/localedata-services/sun.util.locale.provider.LocaleDataMetaInfo
+	$(install-file)
+
+$(eval $(call SetupArchive,BUILD_LOCALEDATA_JAR, \
+    $(LOCALEDATA_METAINF_SERVICES), \
+    SRCS := $(JDK_OUTPUTDIR)/modules/jdk.localedata \
+        $(LOCALEDATA_SERVICES_DIR), \
     SUFFIXES := .class _dict _th, \
     INCLUDES := $(LOCALEDATA_INCLUDES), \
+    EXTRA_FILES := META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo, \
     JAR := $(IMAGES_OUTPUTDIR)/lib/ext/localedata.jar, \
     SKIP_METAINF := true))
 
@@ -210,6 +221,8 @@
     sun/tools/tree \
     sun/tools/util \
     sun/util/cldr/CLDRLocaleDataMetaInfo.class \
+    sun/util/resources/provider/NonEnLocaleDataMetaInfo.class \
+    META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo \
     sun/util/resources/cldr \
     $(LOCALEDATA_INCLUDES) \
     com/oracle/jrockit/jfr \
@@ -429,13 +442,23 @@
 
 CLDRDATA_JAR_DST := $(IMAGES_OUTPUTDIR)/lib/ext/cldrdata.jar
 
-$(eval $(call SetupArchive,BUILD_CLDRDATA_JAR, , \
+CLDR_SERVICES_DIR := $(IMAGES_OUTPUTDIR)/cldrmetainfo
+
+CLDR_METAINF_SERVICES := $(CLDR_SERVICES_DIR)/META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo
+
+$(CLDR_METAINF_SERVICES): $(JDK_TOPDIR)/src/jdk.localedata/META-INF/cldrdata-services/sun.util.locale.provider.LocaleDataMetaInfo
+	$(install-file)
+
+$(eval $(call SetupArchive,BUILD_CLDRDATA_JAR, \
+    $(CLDR_METAINF_SERVICES), \
     SRCS := $(JDK_OUTPUTDIR)/modules/jdk.localedata \
-        $(JDK_OUTPUTDIR)/modules/java.base, \
+        $(JDK_OUTPUTDIR)/modules/java.base \
+        $(CLDR_SERVICES_DIR), \
     SUFFIXES := .class, \
     INCLUDES := sun/text/resources/cldr \
         sun/util/cldr/CLDRLocaleDataMetaInfo.class \
         sun/util/resources/cldr, \
+    EXTRA_FILES := META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo, \
     JAR := $(CLDRDATA_JAR_DST), \
     EXTRA_MANIFEST_ATTR := CLDR-Version: $(CLDRVERSION), \
     SKIP_METAINF := true))
--- a/jdk/make/gensrc/GensrcLocaleDataMetaInfo.gmk	Tue Sep 02 11:21:29 2014 -0700
+++ b/jdk/make/gensrc/GensrcLocaleDataMetaInfo.gmk	Tue Sep 02 14:11:38 2014 -0700
@@ -58,7 +58,14 @@
 # Locales that don't have any resource files should be included here.
 ALL_NON_EN_LOCALES := ja-JP-JP nb-NO nn-NO th-TH-TH
 
-SED_ARGS := -e 's|$(HASH)warn This file is preprocessed before being compiled|// -- This file was mechanically generated: Do not edit! -- //|g'
+SED_ENARGS := -e 's|$(HASH)warn This file is preprocessed before being compiled|// -- This file was mechanically generated: Do not edit! -- //|g'
+SED_NONENARGS := $(SED_ENARGS)
+
+# Fill in the languages and package names
+SED_ENARGS += -e 's/$(HASH)Lang$(HASH)/En/' \
+    -e 's/$(HASH)Package$(HASH)/sun.util.locale.provider/'
+SED_NONENARGS += -e 's/$(HASH)Lang$(HASH)/NonEn/' \
+    -e 's/$(HASH)Package$(HASH)/sun.util.resources.provider/'
 
 # This macro creates a sed expression that substitues for example:
 # #FormatData_ENLocales# with: en% locales.
@@ -78,8 +85,8 @@
   ALL_NON_EN_LOCALES += $$($1_NON_EN_LOCALES)
 
   # Don't sed in a space if there are no locales.
-  SED_ARGS += -e 's/$$(HASH)$1_ENLocales$$(HASH)/$$(if $$($1_EN_LOCALES),$$(SPACE)$$($1_EN_LOCALES),)/g'
-  SED_ARGS += -e 's/$$(HASH)$1_NonENLocales$$(HASH)/$$(if $$($1_NON_EN_LOCALES),$$(SPACE)$$($1_NON_EN_LOCALES),)/g'
+  SED_ENARGS += -e 's/$$(HASH)$1_Locales$$(HASH)/$$(if $$($1_EN_LOCALES),$$(SPACE)$$($1_EN_LOCALES),)/g'
+  SED_NONENARGS += -e 's/$$(HASH)$1_Locales$$(HASH)/$$(if $$($1_NON_EN_LOCALES),$$(SPACE)$$($1_NON_EN_LOCALES),)/g'
 endef
 
 #sun.text.resources.FormatData
@@ -106,17 +113,25 @@
 #sun.util.resources.CalendarData
 $(eval $(call CaptureLocale,CalendarData))
 
-SED_ARGS += -e 's/$(HASH)AvailableLocales_ENLocales$(HASH)/$(sort $(ALL_EN_LOCALES))/g'
-SED_ARGS += -e 's/$(HASH)AvailableLocales_NonENLocales$(HASH)/$(sort $(ALL_NON_EN_LOCALES))/g'
+SED_ENARGS += -e 's/$(HASH)AvailableLocales_Locales$(HASH)/$(sort $(ALL_EN_LOCALES))/g'
+SED_NONENARGS += -e 's/$(HASH)AvailableLocales_Locales$(HASH)/$(sort $(ALL_NON_EN_LOCALES))/g'
 
-$(JDK_OUTPUTDIR)/gensrc/java.base/sun/util/locale/provider/LocaleDataMetaInfo.java: \
+$(JDK_OUTPUTDIR)/gensrc/java.base/sun/util/locale/provider/EnLocaleDataMetaInfo.java: \
     $(JDK_TOPDIR)/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo-XLocales.java.template
 	$(MKDIR) -p $(@D)
-	$(ECHO) Creating sun/util/LocaleDataMetaInfo.java from $(words $(LOCALE_RESOURCES)) found resources.
+	$(ECHO) Creating sun/util/locale/provider/EnLocaleDataMetaInfo.java from $(words $(LOCALE_RESOURCES)) found resources.
 	$(PRINTF) "PREV_LOCALE_RESOURCES:=$(LOCALE_RESOURCES)" > $(JDK_OUTPUTDIR)/gensrc/_the.locale_resources
-	$(SED) $(SED_ARGS) $< > $@
+	$(SED) $(SED_ENARGS) $< > $@
 
-GENSRC_LOCALEDATAMETAINFO := $(JDK_OUTPUTDIR)/gensrc/java.base/sun/util/locale/provider/LocaleDataMetaInfo.java
+$(JDK_OUTPUTDIR)/gensrc/jdk.localedata/sun/util/resources/provider/NonEnLocaleDataMetaInfo.java: \
+    $(JDK_TOPDIR)/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo-XLocales.java.template
+	$(MKDIR) -p $(@D)
+	$(ECHO) Creating sun/util/resources/provider/NonEnLocaleDataMetaInfo.java from $(words $(LOCALE_RESOURCES)) found resources.
+	$(PRINTF) "PREV_LOCALE_RESOURCES:=$(LOCALE_RESOURCES)" > $(JDK_OUTPUTDIR)/gensrc/_the.locale_resources
+	$(SED) $(SED_NONENARGS) $< > $@
+
+GENSRC_LOCALEDATAMETAINFO := $(JDK_OUTPUTDIR)/gensrc/java.base/sun/util/locale/provider/EnLocaleDataMetaInfo.java \
+    $(JDK_OUTPUTDIR)/gensrc/jdk.localedata/sun/util/resources/provider/NonEnLocaleDataMetaInfo.java
 
 ################################################################################
 
--- a/jdk/make/src/classes/build/tools/cldrconverter/CLDRConverter.java	Tue Sep 02 11:21:29 2014 -0700
+++ b/jdk/make/src/classes/build/tools/cldrconverter/CLDRConverter.java	Tue Sep 02 14:11:38 2014 -0700
@@ -431,7 +431,7 @@
             allLocales.addAll(metaInfo.get("LocaleNames"));
             allLocales.addAll(metaInfo.get("CalendarData"));
             allLocales.addAll(metaInfo.get("FormatData"));
-            metaInfo.put("All", allLocales);
+            metaInfo.put("AvailableLocales", allLocales);
         }
 
         bundleGenerator.generateMetaInfo(metaInfo);
--- a/jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java	Tue Sep 02 11:21:29 2014 -0700
+++ b/jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java	Tue Sep 02 14:11:38 2014 -0700
@@ -159,8 +159,10 @@
             out.println(CopyrightHeaders.getOpenJDKCopyright());
 
             out.println("package sun.util.cldr;\n\n"
-                      + "import java.util.ListResourceBundle;\n");
-            out.printf("public class %s extends ListResourceBundle {\n", METAINFO_CLASS);
+                      + "import java.util.ListResourceBundle;\n"
+                      + "import sun.util.locale.provider.LocaleProviderAdapter;\n"
+                      + "import sun.util.locale.provider.LocaleDataMetaInfo;\n");
+            out.printf("public class %s extends ListResourceBundle implements LocaleDataMetaInfo {\n", METAINFO_CLASS);
             out.println("    @Override\n" +
                         "    protected final Object[][] getContents() {\n" +
                         "        final Object[][] data = new Object[][] {");
@@ -168,7 +170,15 @@
                 out.printf("            { \"%s\",\n", key);
                 out.printf("              \"%s\" },\n", toLocaleList(metaInfo.get(key)));
             }
-            out.println("        };\n        return data;\n    }\n}");
+            out.println("        };\n        return data;\n    }\n\n");
+
+            out.println("    public LocaleProviderAdapter.Type getType() {\n" +
+                        "        return LocaleProviderAdapter.Type.CLDR;\n" +
+                        "    }\n\n");
+
+            out.println("    public String availableLanguageTags(String category) {\n" +
+                        "        return getString(category);\n" +
+                        "    };\n}");
         }
     }
 
--- a/jdk/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java	Tue Sep 02 11:21:29 2014 -0700
+++ b/jdk/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java	Tue Sep 02 14:11:38 2014 -0700
@@ -25,20 +25,20 @@
 
 package sun.util.cldr;
 
-import java.io.File;
 import java.security.AccessController;
-import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.text.spi.BreakIteratorProvider;
 import java.text.spi.CollatorProvider;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Locale;
-import java.util.ResourceBundle;
+import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.StringTokenizer;
-import java.util.spi.TimeZoneNameProvider;
 import sun.util.locale.provider.JRELocaleProviderAdapter;
 import sun.util.locale.provider.LocaleProviderAdapter;
+import sun.util.locale.provider.LocaleDataMetaInfo;
 
 /**
  * LocaleProviderAdapter implementation for the CLDR locale data.
@@ -47,26 +47,31 @@
  * @author Naoto Sato
  */
 public class CLDRLocaleProviderAdapter extends JRELocaleProviderAdapter {
-    private static final String LOCALE_DATA_JAR_NAME = "cldrdata.jar";
+
+    private final LocaleDataMetaInfo metaInfo;
 
     public CLDRLocaleProviderAdapter() {
-        final String sep = File.separator;
-        String localeDataJar = java.security.AccessController.doPrivileged(
-                    new sun.security.action.GetPropertyAction("java.home"))
-                + sep + "lib" + sep + "ext" + sep + LOCALE_DATA_JAR_NAME;
-
-        // Peek at the installed extension directory to see if the jar file for
-        // CLDR resources is installed or not.
-        final File f = new File(localeDataJar);
-        boolean result = AccessController.doPrivileged(
-                new PrivilegedAction<Boolean>() {
+        try {
+            metaInfo = AccessController.doPrivileged(new PrivilegedExceptionAction<LocaleDataMetaInfo>() {
                     @Override
-                    public Boolean run() {
-                        return f.exists();
+                public LocaleDataMetaInfo run() {
+                    for (LocaleDataMetaInfo ldmi : ServiceLoader.loadInstalled(LocaleDataMetaInfo.class)) {
+                        if (ldmi.getType() == LocaleProviderAdapter.Type.CLDR) {
+                            return ldmi;
+                        }
+                    }
+                    return null;
                     }
                 });
-        if (!result) {
-            throw new UnsupportedOperationException();
+        }  catch (Exception e) {
+            // Catch any exception, and fail gracefully as if CLDR locales do not exist.
+            // It's ok ignore it if something wrong happens because there always is the
+            // JRE or FALLBACK LocaleProviderAdapter that will do the right thing.
+            throw new UnsupportedOperationException(e);
+        }
+
+        if (metaInfo == null) {
+            throw new UnsupportedOperationException("CLDR locale data could not be found.");
         }
     }
 
@@ -91,7 +96,7 @@
 
     @Override
     public Locale[] getAvailableLocales() {
-        Set<String> all = createLanguageTagSet("All");
+        Set<String> all = createLanguageTagSet("AvailableLocales");
         Locale[] locs = new Locale[all.size()];
         int index = 0;
         for (String tag : all) {
@@ -102,11 +107,10 @@
 
     @Override
     protected Set<String> createLanguageTagSet(String category) {
-        ResourceBundle rb = ResourceBundle.getBundle("sun.util.cldr.CLDRLocaleDataMetaInfo", Locale.ROOT);
-        if (rb.containsKey(category)) {
+        String supportedLocaleString = metaInfo.availableLanguageTags(category);
+        if (supportedLocaleString == null) {
             return Collections.emptySet();
         }
-        String supportedLocaleString = rb.getString(category);
         Set<String> tagset = new HashSet<>();
         StringTokenizer tokens = new StringTokenizer(supportedLocaleString);
         while (tokens.hasMoreTokens()) {
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java	Tue Sep 02 11:21:29 2014 -0700
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java	Tue Sep 02 14:11:38 2014 -0700
@@ -56,7 +56,7 @@
     /**
      * SPI implementations map
      */
-    private ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProvider> providersMap =
+    private final ConcurrentMap<Class<? extends LocaleServiceProvider>, LocaleServiceProvider> providersMap =
             new ConcurrentHashMap<>();
 
     /**
@@ -167,7 +167,6 @@
                     avail.addAll(Arrays.asList(lsp.getAvailableLocales()));
                 }
             }
-            availableLocales = avail.toArray(new Locale[0]);
         }
 
         // assuming caller won't mutate the array.
@@ -178,7 +177,7 @@
      * A dummy locale service provider that indicates there is no
      * provider available
      */
-    private static NullProvider NULL_PROVIDER = new NullProvider();
+    private static final NullProvider NULL_PROVIDER = new NullProvider();
     private static class NullProvider extends LocaleServiceProvider {
         @Override
         public Locale[] getAvailableLocales() {
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java	Tue Sep 02 11:21:29 2014 -0700
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java	Tue Sep 02 14:11:38 2014 -0700
@@ -25,9 +25,9 @@
 
 package sun.util.locale.provider;
 
-import java.io.File;
 import java.security.AccessController;
-import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.text.spi.BreakIteratorProvider;
 import java.text.spi.CollatorProvider;
 import java.text.spi.DateFormatProvider;
@@ -37,6 +37,7 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Locale;
+import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.concurrent.ConcurrentHashMap;
@@ -58,8 +59,6 @@
  */
 public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements ResourceBundleBasedAdapter {
 
-    private static final String LOCALE_DATA_JAR_NAME = "localedata.jar";
-
     private final ConcurrentMap<String, Set<String>> langtagSets
         = new ConcurrentHashMap<>();
 
@@ -356,26 +355,56 @@
     }
 
     protected Set<String> createLanguageTagSet(String category) {
-        String supportedLocaleString = LocaleDataMetaInfo.getSupportedLocaleString(category);
+        String supportedLocaleString = createSupportedLocaleString(category);
         if (supportedLocaleString == null) {
             return Collections.emptySet();
         }
         Set<String> tagset = new HashSet<>();
         StringTokenizer tokens = new StringTokenizer(supportedLocaleString);
         while (tokens.hasMoreTokens()) {
-            String token = tokens.nextToken();
-            if (token.equals("|")) {
-                if (isNonENLangSupported()) {
-                    continue;
-                }
-                break;
-            }
-            tagset.add(token);
+            tagset.add(tokens.nextToken());
         }
 
         return tagset;
     }
 
+    private static String createSupportedLocaleString(String category) {
+        // Directly call English tags, as we know it's in the base module.
+        String supportedLocaleString = EnLocaleDataMetaInfo.getSupportedLocaleString(category);
+
+        // Use ServiceLoader to dynamically acquire installed locales' tags.
+        try {
+            String nonENTags = AccessController.doPrivileged(new PrivilegedExceptionAction<String>() {
+                @Override
+                public String run() {
+                    String tags = null;
+                    for (LocaleDataMetaInfo ldmi :
+                         ServiceLoader.loadInstalled(LocaleDataMetaInfo.class)) {
+                        if (ldmi.getType() == LocaleProviderAdapter.Type.JRE) {
+                            String t = ldmi.availableLanguageTags(category);
+                            if (t != null) {
+                                if (tags == null) {
+                                    tags = t;
+                                } else {
+                                    tags += " " + t;
+                                }
+                            }
+                        }
+                    }
+                    return tags;
+                }
+            });
+
+            if (nonENTags != null) {
+                supportedLocaleString += " " + nonENTags;
+            }
+        }  catch (Exception e) {
+            // catch any exception, and ignore them as if non-EN locales do not exist.
+        }
+
+        return supportedLocaleString;
+    }
+
     /**
      * Lazy load available locales.
      */
@@ -387,27 +416,17 @@
 
     private static Locale[] createAvailableLocales() {
         /*
-         * Gets the locale string list from LocaleDataMetaInfo class and then
+         * Gets the locale string list from LocaleDataMetaInfo classes and then
          * contructs the Locale array and a set of language tags based on the
          * locale string returned above.
          */
-        String supportedLocaleString = LocaleDataMetaInfo.getSupportedLocaleString("AvailableLocales");
+        String supportedLocaleString = createSupportedLocaleString("AvailableLocales");
 
         if (supportedLocaleString.length() == 0) {
             throw new InternalError("No available locales for JRE");
         }
 
-        /*
-         * Look for "|" and construct a new locale string list.
-         */
-        int barIndex = supportedLocaleString.indexOf('|');
-        StringTokenizer localeStringTokenizer;
-        if (isNonENLangSupported()) {
-            localeStringTokenizer = new StringTokenizer(supportedLocaleString.substring(0, barIndex)
-                    + supportedLocaleString.substring(barIndex + 1));
-        } else {
-            localeStringTokenizer = new StringTokenizer(supportedLocaleString.substring(0, barIndex));
-        }
+        StringTokenizer localeStringTokenizer = new StringTokenizer(supportedLocaleString);
 
         int length = localeStringTokenizer.countTokens();
         Locale[] locales = new Locale[length + 1];
@@ -430,39 +449,4 @@
         }
         return locales;
     }
-
-    private static volatile Boolean isNonENSupported = null;
-
-    /*
-     * Returns true if the non EN resources jar file exists in jre
-     * extension directory. @returns true if the jar file is there. Otherwise,
-     * returns false.
-     */
-    private static boolean isNonENLangSupported() {
-        if (isNonENSupported == null) {
-            synchronized (JRELocaleProviderAdapter.class) {
-                if (isNonENSupported == null) {
-                    final String sep = File.separator;
-                    String localeDataJar =
-                            java.security.AccessController.doPrivileged(
-                            new sun.security.action.GetPropertyAction("java.home"))
-                            + sep + "lib" + sep + "ext" + sep + LOCALE_DATA_JAR_NAME;
-
-                    /*
-                     * Peek at the installed extension directory to see if
-                     * localedata.jar is installed or not.
-                     */
-                    final File f = new File(localeDataJar);
-                    isNonENSupported =
-                        AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
-                            @Override
-                            public Boolean run() {
-                                return f.exists();
                             }
-                        });
-               }
-            }
-        }
-        return isNonENSupported;
-    }
-}
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo-XLocales.java.template	Tue Sep 02 11:21:29 2014 -0700
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo-XLocales.java.template	Tue Sep 02 14:11:38 2014 -0700
@@ -30,19 +30,17 @@
  * each resource in sun.util.resources & sun.text.resources.
  * It is used to avoid loading non-existent localized resources so that
  * jar files won't be opened unnecessary to look up them.
- *
- * @since 1.6
  */
-package sun.util.locale.provider;
+package #Package#;
 
 import java.util.HashMap;
-
-
-public class LocaleDataMetaInfo {
+import java.util.Map;
+import sun.util.locale.provider.LocaleDataMetaInfo;
+import static sun.util.locale.provider.LocaleProviderAdapter.Type;
 
-    private static final HashMap<String, String> resourceNameToLocales =
-        new HashMap<String, String>(7);
+public class #Lang#LocaleDataMetaInfo implements LocaleDataMetaInfo {
 
+    private static final Map<String, String> resourceNameToLocales = new HashMap<>(9);
 
     static {
         /* During JDK build time, #XXX_YYY# will be replaced by a string contain all the locales
@@ -52,38 +50,51 @@
            look up locale string such as "en" could be based on if it contains " en ".
         */
         resourceNameToLocales.put("FormatData",
-                                  " #FormatData_ENLocales# | #FormatData_NonENLocales# ");
+                                  " #FormatData_Locales# ");
 
         resourceNameToLocales.put("CollationData",
-                                  " #CollationData_ENLocales# | #CollationData_NonENLocales# ");
+                                  " #CollationData_Locales# ");
 
         resourceNameToLocales.put("BreakIteratorInfo",
-                                  " #BreakIteratorInfo_ENLocales# | #BreakIteratorInfo_NonENLocales# ");
+                                  " #BreakIteratorInfo_Locales# ");
 
         resourceNameToLocales.put("BreakIteratorRules",
-                                  " #BreakIteratorRules_ENLocales# | #BreakIteratorRules_NonENLocales# ");
+                                  " #BreakIteratorRules_Locales# ");
 
         resourceNameToLocales.put("TimeZoneNames",
-                                  " #TimeZoneNames_ENLocales# | #TimeZoneNames_NonENLocales# ");
+                                  " #TimeZoneNames_Locales# ");
 
         resourceNameToLocales.put("LocaleNames",
-                                  " #LocaleNames_ENLocales# | #LocaleNames_NonENLocales# ");
+                                  " #LocaleNames_Locales# ");
 
         resourceNameToLocales.put("CurrencyNames",
-                                  " #CurrencyNames_ENLocales# | #CurrencyNames_NonENLocales# ");
+                                  " #CurrencyNames_Locales# ");
 
         resourceNameToLocales.put("CalendarData",
-                                  " #CalendarData_ENLocales# | #CalendarData_NonENLocales# ");
+                                  " #CalendarData_Locales# ");
 
         resourceNameToLocales.put("AvailableLocales",
-                                  " #AvailableLocales_ENLocales# | #AvailableLocales_NonENLocales# ");
+                                  " #AvailableLocales_Locales# ");
     }
 
     /*
+     * Gets the supported locales string based on the availability of
+     * locale data resource bundles for each resource name.
+     *
      * @param resourceName the resource name
      * @return the supported locale string for the passed in resource.
      */
     public static String getSupportedLocaleString(String resourceName) {
-        return resourceNameToLocales.get(resourceName);
+        return resourceNameToLocales.getOrDefault(resourceName, "");
+    }
+
+    @Override
+    public Type getType() {
+        return Type.JRE;
+}
+
+    @Override
+    public String availableLanguageTags(String category) {
+        return getSupportedLocaleString(category);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo.java	Tue Sep 02 14:11:38 2014 -0700
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.util.locale.provider;
+
+/**
+ * LocaleData meta info SPI
+ *
+ * @author Naoto Sato
+ */
+public interface LocaleDataMetaInfo {
+
+    /**
+     * Returns the type of LocaleProviderAdapter for which this LocaleData
+     * provides the data.
+     * @return type The type of the adapter.
+     */
+    public LocaleProviderAdapter.Type getType();
+
+    /**
+     * Returns the string concatenation of the supported language tags in
+     * this LocaleData instance
+     * @param category category of the locale data.
+     * @return concatenated language tags, separated by a space.
+     */
+    public String availableLanguageTags(String category);
+}
--- a/jdk/src/java.base/share/classes/sun/util/resources/LocaleData.java	Tue Sep 02 11:21:29 2014 -0700
+++ b/jdk/src/java.base/share/classes/sun/util/resources/LocaleData.java	Tue Sep 02 14:11:38 2014 -0700
@@ -50,7 +50,6 @@
 import java.util.ResourceBundle;
 import java.util.Set;
 import sun.util.locale.provider.JRELocaleProviderAdapter;
-import sun.util.locale.provider.LocaleDataMetaInfo;
 import sun.util.locale.provider.LocaleProviderAdapter;
 import static sun.util.locale.provider.LocaleProviderAdapter.Type.CLDR;
 import static sun.util.locale.provider.LocaleProviderAdapter.Type.JRE;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.localedata/META-INF/cldrdata-services/sun.util.locale.provider.LocaleDataMetaInfo	Tue Sep 02 14:11:38 2014 -0700
@@ -0,0 +1,1 @@
+sun.util.cldr.CLDRLocaleDataMetaInfo
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.localedata/META-INF/localedata-services/sun.util.locale.provider.LocaleDataMetaInfo	Tue Sep 02 14:11:38 2014 -0700
@@ -0,0 +1,1 @@
+sun.util.resources.provider.NonEnLocaleDataMetaInfo
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/util/locale/provider/Bug8038436.java	Tue Sep 02 14:11:38 2014 -0700
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8038436
+ * @summary Test for changes in 8038436
+ * @compile -XDignore.symbol.file Bug8038436.java
+ * @run main/othervm Bug8038436 -Djava.ext.dirs=foo security
+ * @run main/othervm Bug8038436 -Djava.locale.providers=JRE availlocs
+ */
+
+import java.security.*;
+import java.text.*;
+import java.util.*;
+import java.util.stream.*;
+import sun.util.locale.provider.*;
+
+public class Bug8038436 {
+    public static void main(String[] args) {
+        switch (args[1]) {
+        case "security":
+            securityTests();
+            break;
+        case "availlocs":
+            availableLocalesTests();
+            break;
+        default:
+            throw new RuntimeException("no test was specified.");
+        }
+    }
+
+    private static void securityTests() {
+        Policy.setPolicy(new MyPolicy());
+        System.setSecurityManager(new SecurityManager());
+
+        /*
+         * Test for AccessClassInPackage security exception. Confirms that
+         * exeption won't be thrown if an application sets a Permission that
+         * does not allow any RuntimePermission, on loading LocaleDataMetaInfo
+         * from jdk.localedata module.
+         */
+        System.out.println(new Formatter(Locale.JAPAN).format("%1$tB %1$te, %1$tY",
+                           new GregorianCalendar()));
+
+        /*
+         * Check only English/ROOT locales are returned if the jdk.localedata
+         * module is not installed (implied by "java.ext.dirs" set to "foo").
+         */
+        if (Arrays.asList(Locale.getAvailableLocales())
+                .stream()
+                .anyMatch(l -> l != Locale.ROOT && l.getLanguage() != "en")) {
+            throw new RuntimeException("non English locale(s) included in available locales");
+        }
+    }
+
+
+    static class MyPolicy extends Policy {
+        final PermissionCollection perms = new Permissions();
+
+        MyPolicy() {
+            // allows no RuntimePermission
+        }
+
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return perms;
+        }
+
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return perms;
+        }
+
+        public boolean implies(ProtectionDomain domain, Permission perm) {
+            return perms.implies(perm);
+        }
+    }
+
+    static final String[] bipLocs = ("ar, ar-JO, ar-LB, ar-SY, be, be-BY, bg, " +
+        "bg-BG, ca, ca-ES, cs, cs-CZ, da, da-DK, de, de-AT, de-CH, de-DE, " +
+        "de-LU, el, el-CY, el-GR, en, en-AU, en-CA, en-GB, en-IE, en-IN, " +
+        "en-MT, en-NZ, en-PH, en-SG, en-US, en-ZA, es, es-AR, es-BO, es-CL, " +
+        "es-CO, es-CR, es-DO, es-EC, es-ES, es-GT, es-HN, es-MX, es-NI, " +
+        "es-PA, es-PE, es-PR, es-PY, es-SV, es-US, es-UY, es-VE, et, et-EE, " +
+        "fi, fi-FI, fr, fr-BE, fr-CA, fr-CH, fr-FR, ga, ga-IE, he, he-IL, " +
+        "hi-IN, hr, hr-HR, hu, hu-HU, id, id-ID, is, is-IS, it, it-CH, it-IT, " +
+        "ja, ja-JP, ko, ko-KR, lt, lt-LT, lv, lv-LV, mk, mk-MK, ms, ms-MY, mt, " +
+        "mt-MT, nl, nl-BE, nl-NL, no, no-NO, no-NO, pl, pl-PL, pt, pt-BR, " +
+        "pt-PT, ro, ro-RO, ru, ru-RU, sk, sk-SK, sl, sl-SI, sq, sq-AL, sr, " +
+        "sr-BA, sr-CS, sr-Latn, sr-Latn-ME, sr-ME, sr-RS, sv, sv-SE, th, th-TH, " +
+        "tr, tr-TR, uk, uk-UA, und, vi, vi-VN, zh, zh-CN, zh-HK, zh-Hans-CN, " +
+        "zh-Hans-SG, zh-Hant-HK, zh-Hant-TW, zh-SG, zh-TW, ").split(",\\s*");
+    static final String[] dfpLocs = bipLocs;
+    static final String[] datefspLocs = bipLocs;
+    static final String[] decimalfspLocs = bipLocs;
+    static final String[] calnpLocs = bipLocs;
+    static final String[] cpLocs = ("ar, be, bg, ca, cs, da, el, es, et, fi, " +
+        "fr, he, hi, hr, hu, is, ja, ko, lt, lv, mk, no, pl, ro, ru, sk, sl, " +
+        "sq, sr, sr-Latn, sv, th, tr, uk, und, vi, zh, zh-HK, zh-Hant-HK, " +
+        "zh-Hant-TW, zh-TW, ").split(",\\s*");
+    static final String[] nfpLocs = ("ar, ar-AE, ar-BH, ar-DZ, ar-EG, ar-IQ, " +
+        "ar-JO, ar-KW, ar-LB, ar-LY, ar-MA, ar-OM, ar-QA, ar-SA, ar-SD, ar-SY, " +
+        "ar-TN, ar-YE, be, be-BY, bg, bg-BG, ca, ca-ES, cs, cs-CZ, da, da-DK, " +
+        "de, de-AT, de-CH, de-DE, de-GR, de-LU, el, el-CY, el-GR, en, en-AU, " +
+        "en-CA, en-GB, en-IE, en-IN, en-MT, en-NZ, en-PH, en-SG, en-US, en-ZA, " +
+        "es, es-AR, es-BO, es-CL, es-CO, es-CR, es-CU, es-DO, es-EC, es-ES, " +
+        "es-GT, es-HN, es-MX, es-NI, es-PA, es-PE, es-PR, es-PY, es-SV, es-US, " +
+        "es-UY, es-VE, et, et-EE, fi, fi-FI, fr, fr-BE, fr-CA, fr-CH, fr-FR, " +
+        "fr-LU, ga, ga-IE, he, he-IL, hi, hi-IN, hr, hr-HR, hu, hu-HU, id, " +
+        "id-ID, is, is-IS, it, it-CH, it-IT, ja, ja-JP, " +
+        "ja-JP-u-ca-japanese-x-lvariant-JP, ko, ko-KR, lt, lt-LT, lv, lv-LV, " +
+        "mk, mk-MK, ms, ms-MY, mt, mt-MT, nb-NO, nl, nl-BE, nl-NL, nn-NO, " +
+        "nn-NO, no, no-NO, pl, pl-PL, pt, pt-BR, pt-PT, ro, ro-RO, ru, ru-RU, " +
+        "sk, sk-SK, sl, sl-SI, sq, sq-AL, sr, sr-BA, sr-CS, sr-Latn, " +
+        "sr-Latn-BA, sr-Latn-ME, sr-Latn-RS, sr-ME, sr-RS, sv, sv-SE, th, " +
+        "th-TH, th-TH-u-nu-thai-x-lvariant-TH, tr, tr-TR, uk, uk-UA, und, vi, " +
+        "vi-VN, zh, zh-CN, zh-HK, zh-Hans-CN, zh-Hans-SG, zh-Hant-HK, " +
+        "zh-Hant-TW, zh-SG, zh-TW, ").split(",\\s*");
+    static final String[] currencynpLocs = ("ar-AE, ar-BH, ar-DZ, ar-EG, ar-IQ, " +
+        "ar-JO, ar-KW, ar-LB, ar-LY, ar-MA, ar-OM, ar-QA, ar-SA, ar-SD, ar-SY, " +
+        "ar-TN, ar-YE, be-BY, bg-BG, ca-ES, cs-CZ, da-DK, de, de-AT, de-CH, " +
+        "de-DE, de-GR, de-LU, el-CY, el-GR, en-AU, en-CA, en-GB, en-IE, en-IN, " +
+        "en-MT, en-NZ, en-PH, en-SG, en-US, en-ZA, es, es-AR, es-BO, es-CL, " +
+        "es-CO, es-CR, es-CU, es-DO, es-EC, es-ES, es-GT, es-HN, es-MX, es-NI, " +
+        "es-PA, es-PE, es-PR, es-PY, es-SV, es-US, es-UY, es-VE, et-EE, fi-FI, " +
+        "fr, fr-BE, fr-CA, fr-CH, fr-FR, fr-LU, ga-IE, he-IL, hi-IN, hr-HR, " +
+        "hu-HU, id-ID, is-IS, it, it-CH, it-IT, ja, ja-JP, ko, ko-KR, lt-LT, " +
+        "lv-LV, mk-MK, ms-MY, mt-MT, nl-BE, nl-NL, no-NO, pl-PL, pt, pt-BR, " +
+        "pt-PT, ro-RO, ru-RU, sk-SK, sl-SI, sq-AL, sr-BA, sr-CS, sr-Latn-BA, " +
+        "sr-Latn-ME, sr-Latn-RS, sr-ME, sr-RS, sv, sv-SE, th-TH, tr-TR, uk-UA, " +
+        "und, vi-VN, zh-CN, zh-HK, zh-Hans-CN, zh-Hans-SG, zh-Hant-HK, " +
+        "zh-Hant-TW, zh-SG, zh-TW, ").split(",\\s*");
+    static final String[] lnpLocs = ("ar, be, bg, ca, cs, da, de, el, el-CY, " +
+        "en, en-MT, en-PH, en-SG, es, es-US, et, fi, fr, ga, he, hi, hr, hu, " +
+        "id, is, it, ja, ko, lt, lv, mk, ms, mt, nl, no, no-NO, pl, pt, pt-BR, " +
+        "pt-PT, ro, ru, sk, sl, sq, sr, sr-Latn, sv, th, tr, uk, und, vi, zh, " +
+        "zh-HK, zh-Hans-SG, zh-Hant-HK, zh-Hant-TW, zh-SG, zh-TW, ").split(",\\s*");
+    static final String[] tznpLocs = ("de, en, en-CA, en-GB, en-IE, es, fr, hi, " +
+        "it, ja, ko, pt-BR, sv, und, zh-CN, zh-HK, zh-Hans-CN, zh-Hant-HK, " +
+        "zh-Hant-TW, zh-TW, ").split(",\\s*");
+    static final String[] caldpLocs = ("ar, be, bg, ca, cs, da, de, el, el-CY, " +
+        "en, en-GB, en-IE, en-MT, es, es-ES, es-US, et, fi, fr, fr-CA, he, hi, " +
+        "hr, hu, id-ID, is, it, ja, ko, lt, lv, mk, ms-MY, mt, mt-MT, nl, no, " +
+        "pl, pt, pt-BR, pt-PT, ro, ru, sk, sl, sq, sr, sr-Latn-BA, sr-Latn-ME, " +
+        "sr-Latn-RS, sv, th, tr, uk, und, vi, zh, ").split(",\\s*");
+    static final String[] calpLocs = caldpLocs;
+
+    /*
+     * Validate whether JRE's *Providers return supported locales list based on
+     * their actual resource bundle exsistence. The above golden data
+     * are manually extracted, so they need to be updated if new locale
+     * data resource bundle were added.
+     */
+    private static void availableLocalesTests() {
+        LocaleProviderAdapter jre = LocaleProviderAdapter.forJRE();
+
+        checkAvailableLocales("BreakIteratorProvider",
+            jre.getBreakIteratorProvider().getAvailableLocales(), bipLocs);
+        checkAvailableLocales("CollatorProvider",
+            jre.getCollatorProvider().getAvailableLocales(), cpLocs);
+        checkAvailableLocales("DateFormatProvider",
+            jre.getDateFormatProvider().getAvailableLocales(), dfpLocs);
+        checkAvailableLocales("DateFormatSymbolsProvider",
+            jre.getDateFormatSymbolsProvider().getAvailableLocales(), datefspLocs);
+        checkAvailableLocales("DecimalFormatSymbolsProvider",
+            jre.getDecimalFormatSymbolsProvider().getAvailableLocales(), decimalfspLocs);
+        checkAvailableLocales("NumberFormatProvider",
+            jre.getNumberFormatProvider().getAvailableLocales(), nfpLocs);
+        checkAvailableLocales("CurrencyNameProvider",
+            jre.getCurrencyNameProvider().getAvailableLocales(), currencynpLocs);
+        checkAvailableLocales("LocaleNameProvider",
+            jre.getLocaleNameProvider().getAvailableLocales(), lnpLocs);
+        checkAvailableLocales("TimeZoneNameProvider",
+            jre.getTimeZoneNameProvider().getAvailableLocales(), tznpLocs);
+        checkAvailableLocales("CalendarDataProvider",
+            jre.getCalendarDataProvider().getAvailableLocales(), caldpLocs);
+        checkAvailableLocales("CalendarNameProvider",
+            jre.getCalendarNameProvider().getAvailableLocales(), calnpLocs);
+        checkAvailableLocales("CalendarProvider",
+            jre.getCalendarProvider().getAvailableLocales(), calpLocs);
+    }
+
+    private static void checkAvailableLocales(String testName, Locale[] got, String[] expected) {
+        System.out.println("Testing available locales for " + testName);
+        List<Locale> gotList = Arrays.asList(got).stream()
+            .map(Locale::toLanguageTag)
+            .sorted()
+            .map(Locale::forLanguageTag)
+            .collect(Collectors.toList());
+        List<Locale> expectedList = Arrays.asList(expected).stream()
+            .map(Locale::forLanguageTag)
+            .collect(Collectors.toList());
+
+        if (!gotList.equals(expectedList)) {
+            throw new RuntimeException("\n" + gotList.toString() + "\n is not equal to \n" +
+                                       expectedList.toString());
+        }
+    }
+}