From 6485051ed1a067eb84a70f4ccff2e496ea642ffd Mon Sep 17 00:00:00 2001 From: okutsu Date: Tue, 19 Jun 2012 16:21:17 +0900 Subject: [PATCH] 6380549: (rb) ResourceBundle.Control global binding support Reviewed-by: naoto --- make/java/java/FILES_java.gmk | 1 + .../classes/java/util/ResourceBundle.java | 60 +++++++++-- .../spi/ResourceBundleControlProvider.java | 65 ++++++++++++ .../UserDefaultControlTest.java | 59 +++++++++++ .../UserDefaultControlTest.sh | 24 +++++ .../providersrc/Makefile | 63 ++++++++++++ .../providersrc/UserControlProvider.java | 45 +++++++++ .../providersrc/UserXMLControl.java | 95 ++++++++++++++++++ .../providersrc/XmlRB.xml | 40 ++++++++ .../providersrc/XmlRB_ja.xml | 40 ++++++++ ...ava.util.spi.ResourceBundleControlProvider | 1 + .../rbcontrolprovider.jar | Bin 0 -> 5825 bytes 12 files changed, 485 insertions(+), 8 deletions(-) create mode 100644 src/share/classes/java/util/spi/ResourceBundleControlProvider.java create mode 100644 test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.java create mode 100644 test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.sh create mode 100644 test/java/util/spi/ResourceBundleControlProvider/providersrc/Makefile create mode 100644 test/java/util/spi/ResourceBundleControlProvider/providersrc/UserControlProvider.java create mode 100644 test/java/util/spi/ResourceBundleControlProvider/providersrc/UserXMLControl.java create mode 100644 test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB.xml create mode 100644 test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB_ja.xml create mode 100644 test/java/util/spi/ResourceBundleControlProvider/providersrc/java.util.spi.ResourceBundleControlProvider create mode 100644 test/java/util/spi/ResourceBundleControlProvider/rbcontrolprovider.jar diff --git a/make/java/java/FILES_java.gmk b/make/java/java/FILES_java.gmk index ddcc92e78..9842527f0 100644 --- a/make/java/java/FILES_java.gmk +++ b/make/java/java/FILES_java.gmk @@ -372,6 +372,7 @@ JAVA_JAVA_java = \ java/util/spi/CurrencyNameProvider.java \ java/util/spi/LocaleNameProvider.java \ java/util/spi/LocaleServiceProvider.java \ + java/util/spi/ResourceBundleControlProvider.java \ java/util/spi/TimeZoneNameProvider.java \ java/io/Closeable.java \ java/io/Flushable.java \ diff --git a/src/share/classes/java/util/ResourceBundle.java b/src/share/classes/java/util/ResourceBundle.java index 6be14d590..6d341dc30 100644 --- a/src/share/classes/java/util/ResourceBundle.java +++ b/src/share/classes/java/util/ResourceBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -55,6 +55,7 @@ import java.security.PrivilegedExceptionAction; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.jar.JarEntry; +import java.util.spi.ResourceBundleControlProvider; import sun.util.locale.BaseLocale; import sun.util.locale.LocaleObjectCache; @@ -192,6 +193,17 @@ import sun.util.locale.LocaleObjectCache; * {@link #getBundle(String, Locale, ClassLoader, Control) getBundle} * factory method for details. * + *

For the {@code getBundle} factory + * methods that take no {@link Control} instance, their default behavior of resource bundle loading + * can be modified with installed {@link + * ResourceBundleControlProvider} implementations. Any installed providers are + * detected at the {@code ResourceBundle} class loading time. If any of the + * providers provides a {@link Control} for the given base name, that {@link + * Control} will be used instead of the default {@link Control}. If there is + * more than one service provider installed for supporting the same base name, + * the first one returned from {@link ServiceLoader} will be used. + * *

Cache Management

* * Resource bundle instances created by the getBundle factory @@ -294,8 +306,7 @@ public abstract class ResourceBundle { /** * Queue for reference objects referring to class loaders or bundles. */ - private static final ReferenceQueue referenceQueue = - new ReferenceQueue<>(); + private static final ReferenceQueue referenceQueue = new ReferenceQueue<>(); /** * The parent bundle of this bundle. @@ -330,6 +341,21 @@ public abstract class ResourceBundle { */ private volatile Set keySet; + private static final List providers; + + static { + List list = null; + ServiceLoader 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 000000000..a215e8673 --- /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 000000000..ac2ecc859 --- /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 000000000..c6b912356 --- /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 000000000..54ebbd50c --- /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 000000000..519e87361 --- /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 000000000..58809f010 --- /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 000000000..ef00b6466 --- /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 000000000..08b55b21b --- /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 000000000..7c2a19d62 --- /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 GIT binary patch literal 5825 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 gy@*TXKX