serviceLoaders
+ = ServiceLoader.loadInstalled(ResourceBundleControlProvider.class);
+ for (ResourceBundleControlProvider provider : serviceLoaders) {
+ if (list == null) {
+ list = new ArrayList<>();
+ }
+ list.add(provider);
+ }
+ providers = list;
+ }
+
/**
* Sole constructor. (For invocation by subclass constructors, typically
* implicit.)
@@ -725,7 +751,7 @@ public abstract class ResourceBundle {
return getBundleImpl(baseName, Locale.getDefault(),
/* must determine loader here, else we break stack invariant */
getLoader(),
- Control.INSTANCE);
+ getDefaultControl(baseName));
}
/**
@@ -797,7 +823,7 @@ public abstract class ResourceBundle {
return getBundleImpl(baseName, locale,
/* must determine loader here, else we break stack invariant */
getLoader(),
- Control.INSTANCE);
+ getDefaultControl(baseName));
}
/**
@@ -849,9 +875,15 @@ public abstract class ResourceBundle {
* Gets a resource bundle using the specified base name, locale, and class
* loader.
*
- * This method behaves the same as calling
+ *
This method behaves the same as calling
* {@link #getBundle(String, Locale, ClassLoader, Control)} passing a
- * default instance of {@link Control}. The following describes this behavior.
+ * default instance of {@link Control} unless another {@link Control} is
+ * provided with the {@link ResourceBundleControlProvider} SPI. Refer to the
+ * description of modifying the default
+ * behavior.
+ *
+ *
The following describes the default
+ * behavior.
*
*
getBundle
uses the base name, the specified locale, and
* the default locale (obtained from {@link java.util.Locale#getDefault()
@@ -1026,7 +1058,7 @@ public abstract class ResourceBundle {
if (loader == null) {
throw new NullPointerException();
}
- return getBundleImpl(baseName, locale, loader, Control.INSTANCE);
+ return getBundleImpl(baseName, locale, loader, getDefaultControl(baseName));
}
/**
@@ -1247,6 +1279,18 @@ public abstract class ResourceBundle {
return getBundleImpl(baseName, targetLocale, loader, control);
}
+ private static Control getDefaultControl(String baseName) {
+ if (providers != null) {
+ for (ResourceBundleControlProvider provider : providers) {
+ Control control = provider.getControl(baseName);
+ if (control != null) {
+ return control;
+ }
+ }
+ }
+ return Control.INSTANCE;
+ }
+
private static ResourceBundle getBundleImpl(String baseName, Locale locale,
ClassLoader loader, Control control) {
if (locale == null || control == null) {
diff --git a/src/share/classes/java/util/spi/ResourceBundleControlProvider.java b/src/share/classes/java/util/spi/ResourceBundleControlProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..a215e86739989a52a8fe95896681c71eff445a6b
--- /dev/null
+++ b/src/share/classes/java/util/spi/ResourceBundleControlProvider.java
@@ -0,0 +1,65 @@
+/*
+ * 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. 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 java.util.spi;
+
+import java.util.ResourceBundle;
+
+/**
+ * An interface for service providers that provide implementations of {@link
+ * java.util.ResourceBundle.Control}. The default resource bundle loading
+ * behavior of the {@code ResourceBundle.getBundle} factory methods that take
+ * no {@link java.util.ResourceBundle.Control} instance can be modified with {@code
+ * ResourceBundleControlProvider} implementations.
+ *
+ *
Provider implementations must be packaged using the Java Extension
+ * Mechanism as installed extensions. Refer to {@link java.util.ServiceLoader}
+ * for the extension packaging. Any installed {@code
+ * ResourceBundleControlProvider} implementations are loaded using {@link
+ * java.util.ServiceLoader} at the {@code ResourceBundle} class loading time.
+ *
+ * @author Masayoshi Okutsu
+ * @since 1.8
+ * @see ResourceBundle#getBundle(String, java.util.Locale, ClassLoader, ResourceBundle.Control)
+ * ResourceBundle.getBundle
+ * @see java.util.ServiceLoader#loadInstalled(Class)
+ */
+public interface ResourceBundleControlProvider {
+ /**
+ * Returns a {@code ResourceBundle.Control} instance that is used
+ * to handle resource bundle loading for the given {@code
+ * baseName}. This method must return {@code null} if the given
+ * {@code baseName} isn't handled by this provider.
+ *
+ * @param baseName the base name of the resource bundle
+ * @return a {@code ResourceBundle.Control} instance,
+ * or {@code null} if the given {@code baseName} is not
+ * applicable to this provider.
+ * @throws NullPointerException if {@code baseName} is {@code null}
+ */
+ public ResourceBundle.Control getControl(String baseName);
+}
diff --git a/test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.java b/test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac2ecc85994b6dd86617b4bb0d51e006e2116bf8
--- /dev/null
+++ b/test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+/*
+ * @test
+ * @bug 6959653
+ * @summary Test ResourceBundle.Control provided using SPI.
+ * @build UserDefaultControlTest
+ * @run shell UserDefaultControlTest.sh
+ */
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+public class UserDefaultControlTest {
+ public static void main(String[] args) {
+ ResourceBundle rb = ResourceBundle.getBundle("com.foo.XmlRB", Locale.ROOT);
+ String type = rb.getString("type");
+ if (!type.equals("XML")) {
+ throw new RuntimeException("Root Locale: type: got " + type
+ + ", expected XML (ASCII)");
+ }
+
+ rb = ResourceBundle.getBundle("com.foo.XmlRB", Locale.JAPAN);
+ type = rb.getString("type");
+ // Expect fullwidth "XML"
+ if (!type.equals("\uff38\uff2d\uff2c")) {
+ throw new RuntimeException("Locale.JAPAN: type: got " + type
+ + ", expected \uff38\uff2d\uff2c (fullwidth XML)");
+ }
+
+ try {
+ rb = ResourceBundle.getBundle("com.bar.XmlRB", Locale.JAPAN);
+ throw new RuntimeException("com.bar.XmlRB test failed.");
+ } catch (MissingResourceException e) {
+ // OK
+ }
+ }
+}
diff --git a/test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.sh b/test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.sh
new file mode 100644
index 0000000000000000000000000000000000000000..c6b912356097c3f029557d19ee3dbe3963e05c73
--- /dev/null
+++ b/test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.sh
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+${TESTJAVA}/bin/java -Djava.ext.dirs=${TESTSRC} -cp ${TESTCLASSES} UserDefaultControlTest
\ No newline at end of file
diff --git a/test/java/util/spi/ResourceBundleControlProvider/providersrc/Makefile b/test/java/util/spi/ResourceBundleControlProvider/providersrc/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..54ebbd50c3a939b6303a549091b59ddd7bf20fc5
--- /dev/null
+++ b/test/java/util/spi/ResourceBundleControlProvider/providersrc/Makefile
@@ -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. 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.
+#
+
+#
+# Makefile for building a ResourceBundleControlProvider jar file for testing.
+#
+# Usage: make JDK_HOME=... all install
+#
+
+DESTDIR = ..
+TMPDIR = tmp
+SERVICESDIR = $(TMPDIR)/META-INF/services
+TARGETJAR = rbcontrolprovider.jar
+BINDIR = $(JDK_HOME)/bin
+
+
+all: $(TARGETJAR)
+
+install: all
+ cp $(TARGETJAR) $(DESTDIR)
+
+SERVICES = java.util.spi.ResourceBundleControlProvider
+
+FILES_JAVA = UserControlProvider.java \
+ UserXMLControl.java
+
+RESOURCE_FILES = XmlRB.xml \
+ XmlRB_ja.xml
+
+$(TARGETJAR): $(SERVICES) $(FILES_JAVA) $(RESOURCE_FILES)
+ rm -rf $(TMPDIR) $@
+ mkdir -p $(SERVICESDIR)
+ $(BINDIR)/javac -d $(TMPDIR) $(FILES_JAVA)
+ cp $(SERVICES) $(SERVICESDIR)
+ cp $(RESOURCE_FILES) $(TMPDIR)/com/foo
+ $(BINDIR)/jar cvf $@ -C $(TMPDIR) .
+
+clean:
+ rm -rf $(TMPDIR) $(TARGETJAR)
+
+.PHONY: all install clean
diff --git a/test/java/util/spi/ResourceBundleControlProvider/providersrc/UserControlProvider.java b/test/java/util/spi/ResourceBundleControlProvider/providersrc/UserControlProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..519e87361b043cbab90ac3bdd4a52c4af2c9feac
--- /dev/null
+++ b/test/java/util/spi/ResourceBundleControlProvider/providersrc/UserControlProvider.java
@@ -0,0 +1,45 @@
+/*
+ * 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. 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 com.foo;
+
+import java.util.ResourceBundle;
+import java.util.spi.ResourceBundleControlProvider;
+
+public class UserControlProvider implements ResourceBundleControlProvider {
+ static final ResourceBundle.Control XMLCONTROL = new UserXMLControl();
+
+ public ResourceBundle.Control getControl(String baseName) {
+ System.out.println(getClass().getName()+".getControl called for " + baseName);
+
+ // Throws a NPE if baseName is null.
+ if (baseName.startsWith("com.foo.Xml")) {
+ System.out.println("\treturns " + XMLCONTROL);
+ return XMLCONTROL;
+ }
+ System.out.println("\treturns null");
+ return null;
+ }
+}
diff --git a/test/java/util/spi/ResourceBundleControlProvider/providersrc/UserXMLControl.java b/test/java/util/spi/ResourceBundleControlProvider/providersrc/UserXMLControl.java
new file mode 100644
index 0000000000000000000000000000000000000000..58809f010433ce1eae88fba6ca3f0e9bf87309a1
--- /dev/null
+++ b/test/java/util/spi/ResourceBundleControlProvider/providersrc/UserXMLControl.java
@@ -0,0 +1,95 @@
+/*
+ * 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.foo;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import static java.util.ResourceBundle.Control.*;
+
+public class UserXMLControl extends ResourceBundle.Control {
+ @Override
+ public List getFormats(String baseName) {
+ if (baseName == null) {
+ throw new NullPointerException();
+ }
+ return Arrays.asList("xml");
+ }
+
+ @Override
+ public ResourceBundle newBundle(String baseName, Locale locale,
+ String format,
+ ClassLoader loader,
+ boolean reload)
+ throws IllegalAccessException,
+ InstantiationException, IOException {
+ if (baseName == null || locale == null
+ || format == null || loader == null) {
+ throw new NullPointerException();
+ }
+ ResourceBundle bundle = null;
+ if (format.equals("xml")) {
+ String bundleName = toBundleName(baseName, locale);
+ String resourceName = toResourceName(bundleName, format);
+ URL url = loader.getResource(resourceName);
+ if (url != null) {
+ URLConnection connection = url.openConnection();
+ if (connection != null) {
+ if (reload) {
+ // disable caches if reloading
+ connection.setUseCaches(false);
+ }
+ try (InputStream stream = connection.getInputStream()) {
+ if (stream != null) {
+ BufferedInputStream bis = new BufferedInputStream(stream);
+ bundle = new XMLResourceBundle(bis);
+ }
+ }
+ }
+ }
+ }
+ return bundle;
+ }
+
+ private static class XMLResourceBundle extends ResourceBundle {
+ private Properties props;
+
+ XMLResourceBundle(InputStream stream) throws IOException {
+ props = new Properties();
+ props.loadFromXML(stream);
+ }
+
+ protected Object handleGetObject(String key) {
+ if (key == null) {
+ throw new NullPointerException();
+ }
+ return props.get(key);
+ }
+
+ public Enumeration getKeys() {
+ // Not implemented
+ return null;
+ }
+ }
+}
diff --git a/test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB.xml b/test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ef00b646698f723a06c5a85acd6c69d07d9a761d
--- /dev/null
+++ b/test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+]>
+
+
+ Test data for UserDefaultControlTest.java
+ XML
+
diff --git a/test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB_ja.xml b/test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB_ja.xml
new file mode 100644
index 0000000000000000000000000000000000000000..08b55b21bf07e5f034a52cc90dd7201921986ae0
--- /dev/null
+++ b/test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB_ja.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+]>
+
+
+ Test data for UserDefaultControlTest.java
+ XML
+
diff --git a/test/java/util/spi/ResourceBundleControlProvider/providersrc/java.util.spi.ResourceBundleControlProvider b/test/java/util/spi/ResourceBundleControlProvider/providersrc/java.util.spi.ResourceBundleControlProvider
new file mode 100644
index 0000000000000000000000000000000000000000..7c2a19d621defca02fd47776ec85254d0383b57e
--- /dev/null
+++ b/test/java/util/spi/ResourceBundleControlProvider/providersrc/java.util.spi.ResourceBundleControlProvider
@@ -0,0 +1 @@
+com.foo.UserControlProvider
diff --git a/test/java/util/spi/ResourceBundleControlProvider/rbcontrolprovider.jar b/test/java/util/spi/ResourceBundleControlProvider/rbcontrolprovider.jar
new file mode 100644
index 0000000000000000000000000000000000000000..b7e6a491d7910a79c5f14b8cba79029b252e64f3
Binary files /dev/null and b/test/java/util/spi/ResourceBundleControlProvider/rbcontrolprovider.jar differ