8000997: Multiple locale sensitive services cannot be loaded
authornaoto
Mon, 29 Oct 2012 10:42:41 -0700
changeset 14331 d3411e624053
parent 14330 e4cb78065603
child 14332 451c5dd717dc
8000997: Multiple locale sensitive services cannot be loaded Reviewed-by: okutsu
jdk/src/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java
jdk/src/share/classes/sun/util/locale/provider/SPILocaleProviderAdapter.java
jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.java
jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.sh
jdk/test/java/util/PluggableLocale/GenericTest.java
jdk/test/java/util/PluggableLocale/barprovider.jar
jdk/test/java/util/PluggableLocale/providersrc/CurrencyNameProviderImpl2.java
jdk/test/java/util/PluggableLocale/providersrc/Makefile
jdk/test/java/util/PluggableLocale/providersrc/java.util.spi.CurrencyNameProvider
--- a/jdk/src/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java	Mon Oct 29 14:12:37 2012 +0100
+++ b/jdk/src/share/classes/sun/util/locale/provider/LocaleServiceProviderPool.java	Mon Oct 29 10:42:41 2012 -0700
@@ -356,7 +356,7 @@
      * @param locale the input locale
      * @return the list of candidate locales for the given locale
      */
-    private static List<Locale> getLookupLocales(Locale locale) {
+    static List<Locale> getLookupLocales(Locale locale) {
         // Note: We currently use the default implementation of
         // ResourceBundle.Control.getCandidateLocales. The result
         // returned by getCandidateLocales are already normalized
--- a/jdk/src/share/classes/sun/util/locale/provider/SPILocaleProviderAdapter.java	Mon Oct 29 14:12:37 2012 +0100
+++ b/jdk/src/share/classes/sun/util/locale/provider/SPILocaleProviderAdapter.java	Mon Oct 29 10:42:41 2012 -0700
@@ -28,8 +28,11 @@
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
-import java.util.ServiceLoader;
-import java.util.spi.LocaleServiceProvider;
+import java.text.*;
+import java.text.spi.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.spi.*;
 
 /**
  * LocaleProviderAdapter implementation for the installed SPI implementations.
@@ -54,11 +57,28 @@
                 @Override
                 @SuppressWarnings("unchecked")
                 public P run() {
-                    P lsp = null;
+                    P delegate = null;
+
                     for (LocaleServiceProvider provider : ServiceLoader.loadInstalled(c)) {
-                        lsp = (P) provider;
+                        if (delegate == null) {
+                            try {
+                                delegate =
+                                    (P) Class.forName(SPILocaleProviderAdapter.class.getCanonicalName() +
+                                              "$" +
+                                              c.getSimpleName() +
+                                              "Delegate")
+                                              .newInstance();
+                            }  catch (ClassNotFoundException |
+                                      InstantiationException |
+                                      IllegalAccessException e) {
+                                LocaleServiceProviderPool.config(SPILocaleProviderAdapter.class, e.toString());
+                                return null;
                     }
-                    return lsp;
+                        }
+
+                        ((Delegate)delegate).addImpl(provider);
+                    }
+                    return delegate;
                 }
             });
         }  catch (PrivilegedActionException e) {
@@ -66,4 +86,478 @@
         }
         return null;
     }
+
+    /*
+     * Delegate interface. All the implementations have to have the class name
+     * following "<provider class name>Delegate" convention.
+     */
+    interface Delegate<P extends LocaleServiceProvider> {
+        public void addImpl(P impl);
+        public P getImpl(Locale locale);
 }
+
+    /*
+     * Obtain the real SPI implementation, using locale fallback
+     */
+    private static <P extends LocaleServiceProvider> P getImpl(Map<Locale, P> map, Locale locale) {
+        for (Locale l : LocaleServiceProviderPool.getLookupLocales(locale)) {
+            P ret = map.get(l);
+            if (ret != null) {
+                return ret;
+            }
+        }
+        return null;
+    }
+
+    /*
+     * Delegates for the actual SPI implementations.
+     */
+    static class BreakIteratorProviderDelegate extends BreakIteratorProvider
+                                        implements Delegate<BreakIteratorProvider> {
+        private ConcurrentMap<Locale, BreakIteratorProvider> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void addImpl(BreakIteratorProvider impl) {
+            for (Locale l : impl.getAvailableLocales()) {
+                map.put(l, impl);
+            }
+        }
+
+        @Override
+        public BreakIteratorProvider getImpl(Locale locale) {
+            return SPILocaleProviderAdapter.getImpl(map, locale);
+        }
+
+        @Override
+        public Locale[] getAvailableLocales() {
+            return map.keySet().toArray(new Locale[0]);
+        }
+
+        @Override
+        public boolean isSupportedLocale(Locale locale) {
+            return map.containsKey(locale);
+        }
+
+        @Override
+        public BreakIterator getWordInstance(Locale locale) {
+            BreakIteratorProvider bip = getImpl(locale);
+            assert bip != null;
+            return bip.getWordInstance(locale);
+        }
+
+        @Override
+        public BreakIterator getLineInstance(Locale locale) {
+            BreakIteratorProvider bip = getImpl(locale);
+            assert bip != null;
+            return bip.getLineInstance(locale);
+        }
+
+        @Override
+        public BreakIterator getCharacterInstance(Locale locale) {
+            BreakIteratorProvider bip = getImpl(locale);
+            assert bip != null;
+            return bip.getCharacterInstance(locale);
+        }
+
+        @Override
+        public BreakIterator getSentenceInstance(Locale locale) {
+            BreakIteratorProvider bip = getImpl(locale);
+            assert bip != null;
+            return bip.getSentenceInstance(locale);
+        }
+
+    }
+
+    static class CollatorProviderDelegate extends CollatorProvider implements Delegate<CollatorProvider> {
+        private ConcurrentMap<Locale, CollatorProvider> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void addImpl(CollatorProvider impl) {
+            for (Locale l : impl.getAvailableLocales()) {
+                map.put(l, impl);
+            }
+        }
+
+        @Override
+        public CollatorProvider getImpl(Locale locale) {
+            return SPILocaleProviderAdapter.getImpl(map, locale);
+        }
+
+        @Override
+        public Locale[] getAvailableLocales() {
+            return map.keySet().toArray(new Locale[0]);
+        }
+
+        @Override
+        public boolean isSupportedLocale(Locale locale) {
+            return map.containsKey(locale);
+        }
+
+        @Override
+        public Collator getInstance(Locale locale) {
+            CollatorProvider cp = getImpl(locale);
+            assert cp != null;
+            return cp.getInstance(locale);
+        }
+    }
+
+    static class DateFormatProviderDelegate extends DateFormatProvider
+                                     implements Delegate<DateFormatProvider> {
+        private ConcurrentMap<Locale, DateFormatProvider> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void addImpl(DateFormatProvider impl) {
+            for (Locale l : impl.getAvailableLocales()) {
+                map.put(l, impl);
+            }
+        }
+
+        @Override
+        public DateFormatProvider getImpl(Locale locale) {
+            return SPILocaleProviderAdapter.getImpl(map, locale);
+        }
+
+        @Override
+        public Locale[] getAvailableLocales() {
+            return map.keySet().toArray(new Locale[0]);
+        }
+
+        @Override
+        public boolean isSupportedLocale(Locale locale) {
+            return map.containsKey(locale);
+        }
+
+        @Override
+        public DateFormat getTimeInstance(int style, Locale locale) {
+            DateFormatProvider dfp = getImpl(locale);
+            assert dfp != null;
+            return dfp.getTimeInstance(style, locale);
+        }
+
+        @Override
+        public DateFormat getDateInstance(int style, Locale locale) {
+            DateFormatProvider dfp = getImpl(locale);
+            assert dfp != null;
+            return dfp.getDateInstance(style, locale);
+        }
+
+        @Override
+        public DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) {
+            DateFormatProvider dfp = getImpl(locale);
+            assert dfp != null;
+            return dfp.getDateTimeInstance(dateStyle, timeStyle, locale);
+        }
+    }
+
+    static class DateFormatSymbolsProviderDelegate extends DateFormatSymbolsProvider
+                                            implements Delegate<DateFormatSymbolsProvider> {
+        private ConcurrentMap<Locale, DateFormatSymbolsProvider> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void addImpl(DateFormatSymbolsProvider impl) {
+            for (Locale l : impl.getAvailableLocales()) {
+                map.put(l, impl);
+            }
+        }
+
+        @Override
+        public DateFormatSymbolsProvider getImpl(Locale locale) {
+            return SPILocaleProviderAdapter.getImpl(map, locale);
+        }
+
+        @Override
+        public Locale[] getAvailableLocales() {
+            return map.keySet().toArray(new Locale[0]);
+        }
+
+        @Override
+        public boolean isSupportedLocale(Locale locale) {
+            return map.containsKey(locale);
+        }
+
+        @Override
+        public DateFormatSymbols getInstance(Locale locale) {
+            DateFormatSymbolsProvider dfsp = getImpl(locale);
+            assert dfsp != null;
+            return dfsp.getInstance(locale);
+        }
+    }
+
+    static class DecimalFormatSymbolsProviderDelegate extends DecimalFormatSymbolsProvider
+                                               implements Delegate<DecimalFormatSymbolsProvider> {
+        private ConcurrentMap<Locale, DecimalFormatSymbolsProvider> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void addImpl(DecimalFormatSymbolsProvider impl) {
+            for (Locale l : impl.getAvailableLocales()) {
+                map.put(l, impl);
+            }
+        }
+
+        @Override
+        public DecimalFormatSymbolsProvider getImpl(Locale locale) {
+            return SPILocaleProviderAdapter.getImpl(map, locale);
+        }
+
+        @Override
+        public Locale[] getAvailableLocales() {
+            return map.keySet().toArray(new Locale[0]);
+        }
+
+        @Override
+        public boolean isSupportedLocale(Locale locale) {
+            return map.containsKey(locale);
+        }
+
+        @Override
+        public DecimalFormatSymbols getInstance(Locale locale) {
+            DecimalFormatSymbolsProvider dfsp = getImpl(locale);
+            assert dfsp != null;
+            return dfsp.getInstance(locale);
+        }
+    }
+
+    static class NumberFormatProviderDelegate extends NumberFormatProvider
+                                       implements Delegate<NumberFormatProvider> {
+        private ConcurrentMap<Locale, NumberFormatProvider> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void addImpl(NumberFormatProvider impl) {
+            for (Locale l : impl.getAvailableLocales()) {
+                map.put(l, impl);
+            }
+        }
+
+        @Override
+        public NumberFormatProvider getImpl(Locale locale) {
+            return SPILocaleProviderAdapter.getImpl(map, locale);
+        }
+
+        @Override
+        public Locale[] getAvailableLocales() {
+            return map.keySet().toArray(new Locale[0]);
+        }
+
+        @Override
+        public boolean isSupportedLocale(Locale locale) {
+            return map.containsKey(locale);
+        }
+
+        @Override
+        public NumberFormat getCurrencyInstance(Locale locale) {
+            NumberFormatProvider nfp = getImpl(locale);
+            assert nfp != null;
+            return nfp.getCurrencyInstance(locale);
+        }
+
+        @Override
+        public NumberFormat getIntegerInstance(Locale locale) {
+            NumberFormatProvider nfp = getImpl(locale);
+            assert nfp != null;
+            return nfp.getIntegerInstance(locale);
+        }
+
+        @Override
+        public NumberFormat getNumberInstance(Locale locale) {
+            NumberFormatProvider nfp = getImpl(locale);
+            assert nfp != null;
+            return nfp.getNumberInstance(locale);
+        }
+
+        @Override
+        public NumberFormat getPercentInstance(Locale locale) {
+            NumberFormatProvider nfp = getImpl(locale);
+            assert nfp != null;
+            return nfp.getPercentInstance(locale);
+        }
+    }
+
+    static class CalendarDataProviderDelegate extends CalendarDataProvider
+                                       implements Delegate<CalendarDataProvider> {
+        private ConcurrentMap<Locale, CalendarDataProvider> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void addImpl(CalendarDataProvider impl) {
+            for (Locale l : impl.getAvailableLocales()) {
+                map.put(l, impl);
+            }
+        }
+
+        @Override
+        public CalendarDataProvider getImpl(Locale locale) {
+            return SPILocaleProviderAdapter.getImpl(map, locale);
+        }
+
+        @Override
+        public Locale[] getAvailableLocales() {
+            return map.keySet().toArray(new Locale[0]);
+        }
+
+        @Override
+        public boolean isSupportedLocale(Locale locale) {
+            return map.containsKey(locale);
+        }
+
+        @Override
+        public int getFirstDayOfWeek(Locale locale) {
+            CalendarDataProvider cdp = getImpl(locale);
+            assert cdp != null;
+            return cdp.getFirstDayOfWeek(locale);
+        }
+
+        @Override
+        public int getMinimalDaysInFirstWeek(Locale locale) {
+            CalendarDataProvider cdp = getImpl(locale);
+            assert cdp != null;
+            return cdp.getMinimalDaysInFirstWeek(locale);
+        }
+
+        @Override
+        public String getDisplayName(String calendarType,
+                                              int field, int value,
+                                              int style, Locale locale) {
+            CalendarDataProvider cdp = getImpl(locale);
+            assert cdp != null;
+            return cdp.getDisplayName(calendarType, field, value, style, locale);
+        }
+
+        @Override
+        public Map<String, Integer> getDisplayNames(String calendarType,
+                                                             int field, int style,
+                                                             Locale locale) {
+            CalendarDataProvider cdp = getImpl(locale);
+            assert cdp != null;
+            return cdp.getDisplayNames(calendarType, field, style, locale);
+        }
+    }
+
+    static class CurrencyNameProviderDelegate extends CurrencyNameProvider
+                                       implements Delegate<CurrencyNameProvider> {
+        private ConcurrentMap<Locale, CurrencyNameProvider> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void addImpl(CurrencyNameProvider impl) {
+            for (Locale l : impl.getAvailableLocales()) {
+                map.put(l, impl);
+            }
+        }
+
+        @Override
+        public CurrencyNameProvider getImpl(Locale locale) {
+            return SPILocaleProviderAdapter.getImpl(map, locale);
+        }
+
+        @Override
+        public Locale[] getAvailableLocales() {
+            return map.keySet().toArray(new Locale[0]);
+        }
+
+        @Override
+        public boolean isSupportedLocale(Locale locale) {
+            return map.containsKey(locale);
+        }
+
+        @Override
+        public String getSymbol(String currencyCode, Locale locale) {
+            CurrencyNameProvider cnp = getImpl(locale);
+            assert cnp != null;
+            return cnp.getSymbol(currencyCode, locale);
+        }
+
+        @Override
+        public String getDisplayName(String currencyCode, Locale locale) {
+            CurrencyNameProvider cnp = getImpl(locale);
+            assert cnp != null;
+            return cnp.getDisplayName(currencyCode, locale);
+        }
+    }
+
+    static class LocaleNameProviderDelegate extends LocaleNameProvider
+                                     implements Delegate<LocaleNameProvider> {
+        private ConcurrentMap<Locale, LocaleNameProvider> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void addImpl(LocaleNameProvider impl) {
+            for (Locale l : impl.getAvailableLocales()) {
+                map.put(l, impl);
+            }
+        }
+
+        @Override
+        public LocaleNameProvider getImpl(Locale locale) {
+            return SPILocaleProviderAdapter.getImpl(map, locale);
+        }
+
+        @Override
+        public Locale[] getAvailableLocales() {
+            return map.keySet().toArray(new Locale[0]);
+        }
+
+        @Override
+        public boolean isSupportedLocale(Locale locale) {
+            return map.containsKey(locale);
+        }
+
+        @Override
+        public String getDisplayLanguage(String languageCode, Locale locale) {
+            LocaleNameProvider lnp = getImpl(locale);
+            assert lnp != null;
+            return lnp.getDisplayLanguage(languageCode, locale);
+        }
+
+        @Override
+        public String getDisplayScript(String scriptCode, Locale locale) {
+            LocaleNameProvider lnp = getImpl(locale);
+            assert lnp != null;
+            return lnp.getDisplayScript(scriptCode, locale);
+        }
+
+        @Override
+        public String getDisplayCountry(String countryCode, Locale locale) {
+            LocaleNameProvider lnp = getImpl(locale);
+            assert lnp != null;
+            return lnp.getDisplayCountry(countryCode, locale);
+        }
+
+        @Override
+        public String getDisplayVariant(String variant, Locale locale) {
+            LocaleNameProvider lnp = getImpl(locale);
+            assert lnp != null;
+            return lnp.getDisplayVariant(variant, locale);
+        }
+    }
+
+    static class TimeZoneNameProviderDelegate extends TimeZoneNameProvider
+                                     implements Delegate<TimeZoneNameProvider> {
+        private ConcurrentMap<Locale, TimeZoneNameProvider> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void addImpl(TimeZoneNameProvider impl) {
+            for (Locale l : impl.getAvailableLocales()) {
+                map.put(l, impl);
+            }
+        }
+
+        @Override
+        public TimeZoneNameProvider getImpl(Locale locale) {
+            return SPILocaleProviderAdapter.getImpl(map, locale);
+        }
+
+        @Override
+        public Locale[] getAvailableLocales() {
+            return map.keySet().toArray(new Locale[0]);
+        }
+
+        @Override
+        public boolean isSupportedLocale(Locale locale) {
+            return map.containsKey(locale);
+        }
+
+        @Override
+        public String getDisplayName(String ID, boolean daylight, int style, Locale locale) {
+            TimeZoneNameProvider tznp = getImpl(locale);
+            assert tznp != null;
+            return tznp.getDisplayName(ID, daylight, style, locale);
+        }
+    }
+}
--- a/jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.java	Mon Oct 29 14:12:37 2012 +0100
+++ b/jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.java	Mon Oct 29 10:42:41 2012 -0700
@@ -48,10 +48,13 @@
 
     void test1() {
         com.bar.CurrencyNameProviderImpl cnp = new com.bar.CurrencyNameProviderImpl();
+        com.bar.CurrencyNameProviderImpl2 cnp2 = new com.bar.CurrencyNameProviderImpl2();
         Locale[] availloc = Locale.getAvailableLocales();
         Locale[] testloc = availloc.clone();
         List<Locale> jreimplloc = Arrays.asList(LocaleProviderAdapter.forJRE().getCurrencyNameProvider().getAvailableLocales());
-        List<Locale> providerloc = Arrays.asList(cnp.getAvailableLocales());
+        List<Locale> providerloc = new ArrayList<Locale>();
+        providerloc.addAll(Arrays.asList(cnp.getAvailableLocales()));
+        providerloc.addAll(Arrays.asList(cnp2.getAvailableLocales()));
 
         for (Locale target: availloc) {
             // pure JRE implementation
@@ -79,8 +82,13 @@
                 String providerscurrency = null;
                 String providersname = null;
                 if (providerloc.contains(target)) {
+                    if (cnp.isSupportedLocale(target)) {
                     providerscurrency = cnp.getSymbol(c.getCurrencyCode(), target);
                     providersname = cnp.getDisplayName(c.getCurrencyCode(), target);
+                    } else {
+                        providerscurrency = cnp2.getSymbol(c.getCurrencyCode(), target);
+                        providersname = cnp2.getDisplayName(c.getCurrencyCode(), target);
+                    }
                 }
 
                 // JRE's name
@@ -109,18 +117,22 @@
     final String pattern = "###,###\u00A4";
     final String YEN_IN_OSAKA = "100,000\u5186\u3084\u3002";
     final String YEN_IN_KYOTO = "100,000\u5186\u3069\u3059\u3002";
+    final String YEN_IN_TOKYO= "100,000JPY-tokyo";
     final Locale OSAKA = new Locale("ja", "JP", "osaka");
     final Locale KYOTO = new Locale("ja", "JP", "kyoto");
+    final Locale TOKYO = new Locale("ja", "JP", "tokyo");
     Integer i = new Integer(100000);
     String formatted;
     DecimalFormat df;
 
     void test2() {
+        Locale defloc = Locale.getDefault();
+
         try {
             df = new DecimalFormat(pattern, DecimalFormatSymbols.getInstance(OSAKA));
             System.out.println(formatted = df.format(i));
             if(!formatted.equals(YEN_IN_OSAKA)) {
-                throw new RuntimeException("formatted zone names mismatch. " +
+                throw new RuntimeException("formatted currency names mismatch. " +
                     "Should match with " + YEN_IN_OSAKA);
             }
 
@@ -130,13 +142,25 @@
             df = new DecimalFormat(pattern, DecimalFormatSymbols.getInstance());
             System.out.println(formatted = df.format(i));
             if(!formatted.equals(YEN_IN_KYOTO)) {
-                throw new RuntimeException("formatted zone names mismatch. " +
+                throw new RuntimeException("formatted currency names mismatch. " +
                     "Should match with " + YEN_IN_KYOTO);
             }
 
             df.parse(YEN_IN_KYOTO);
+
+            Locale.setDefault(TOKYO);
+            df = new DecimalFormat(pattern, DecimalFormatSymbols.getInstance());
+            System.out.println(formatted = df.format(i));
+            if(!formatted.equals(YEN_IN_TOKYO)) {
+                throw new RuntimeException("formatted currency names mismatch. " +
+                    "Should match with " + YEN_IN_TOKYO);
+            }
+
+            df.parse(YEN_IN_TOKYO);
         } catch (ParseException pe) {
             throw new RuntimeException("parse error occured" + pe);
+        } finally {
+            Locale.setDefault(defloc);
         }
     }
 }
--- a/jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.sh	Mon Oct 29 14:12:37 2012 +0100
+++ b/jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.sh	Mon Oct 29 10:42:41 2012 -0700
@@ -23,6 +23,6 @@
 #!/bin/sh
 #
 # @test
-# @bug 4052440
+# @bug 4052440 8000997
 # @summary CurrencyNameProvider tests
 # @run shell ExecTest.sh bar CurrencyNameProviderTest true
--- a/jdk/test/java/util/PluggableLocale/GenericTest.java	Mon Oct 29 14:12:37 2012 +0100
+++ b/jdk/test/java/util/PluggableLocale/GenericTest.java	Mon Oct 29 10:42:41 2012 -0700
@@ -38,6 +38,7 @@
     com.foo.DecimalFormatSymbolsProviderImpl decimalFSP = new com.foo.DecimalFormatSymbolsProviderImpl();
     com.foo.NumberFormatProviderImpl numberFP = new com.foo.NumberFormatProviderImpl();
     com.bar.CurrencyNameProviderImpl currencyNP = new com.bar.CurrencyNameProviderImpl();
+    com.bar.CurrencyNameProviderImpl2 currencyNP2 = new com.bar.CurrencyNameProviderImpl2();
     com.bar.LocaleNameProviderImpl localeNP = new com.bar.LocaleNameProviderImpl();
     com.bar.TimeZoneNameProviderImpl tzNP = new com.bar.TimeZoneNameProviderImpl();
     com.bar.CalendarDataProviderImpl calDataP = new com.bar.CalendarDataProviderImpl();
@@ -68,6 +69,7 @@
         expected.addAll(Arrays.asList(decimalFSP.getAvailableLocales()));
         expected.addAll(Arrays.asList(numberFP.getAvailableLocales()));
         expected.addAll(Arrays.asList(currencyNP.getAvailableLocales()));
+        expected.addAll(Arrays.asList(currencyNP2.getAvailableLocales()));
         expected.addAll(Arrays.asList(localeNP.getAvailableLocales()));
         expected.addAll(Arrays.asList(tzNP.getAvailableLocales()));
         expected.addAll(Arrays.asList(calDataP.getAvailableLocales()));
Binary file jdk/test/java/util/PluggableLocale/barprovider.jar has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/PluggableLocale/providersrc/CurrencyNameProviderImpl2.java	Mon Oct 29 10:42:41 2012 -0700
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012, 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.
+ */
+/*
+ *
+ */
+
+package com.bar;
+
+import java.util.*;
+import java.util.spi.*;
+
+import com.foobar.Utils;
+
+public class CurrencyNameProviderImpl2 extends CurrencyNameProvider {
+    static Locale[] avail = {new Locale("ja", "JP", "tokyo")};
+    public Locale[] getAvailableLocales() {
+        return avail;
+    }
+
+    @Override
+    public String getSymbol(String c, Locale locale) {
+        if (!Utils.supportsLocale(Arrays.asList(avail), locale)) {
+            throw new IllegalArgumentException("locale is not supported: "+locale);
+        }
+
+        if (c.equals("JPY") && Utils.supportsLocale(avail[0], locale)) {
+            return "JPY-tokyo";
+        }
+        return null;
+    }
+
+    @Override
+    public String getDisplayName(String c, Locale locale) {
+        if (!Utils.supportsLocale(Arrays.asList(avail), locale)) {
+            throw new IllegalArgumentException("locale is not supported: "+locale);
+        }
+
+        if (c.equals("JPY") && Utils.supportsLocale(avail[0], locale)) {
+            return "JPY-tokyo";
+        }
+        return null;
+    }
+}
--- a/jdk/test/java/util/PluggableLocale/providersrc/Makefile	Mon Oct 29 14:12:37 2012 +0100
+++ b/jdk/test/java/util/PluggableLocale/providersrc/Makefile	Mon Oct 29 10:42:41 2012 -0700
@@ -35,6 +35,7 @@
 
 BARFILES_JAVA = \
     CurrencyNameProviderImpl.java \
+    CurrencyNameProviderImpl2.java \
     TimeZoneNameProviderImpl.java \
     LocaleNameProviderImpl.java \
     CalendarDataProviderImpl.java \
--- a/jdk/test/java/util/PluggableLocale/providersrc/java.util.spi.CurrencyNameProvider	Mon Oct 29 14:12:37 2012 +0100
+++ b/jdk/test/java/util/PluggableLocale/providersrc/java.util.spi.CurrencyNameProvider	Mon Oct 29 10:42:41 2012 -0700
@@ -5,3 +5,4 @@
 # implementation class
 #
 com.bar.CurrencyNameProviderImpl
+com.bar.CurrencyNameProviderImpl2