8000354: (props) Properties.storeToXML/loadFromXML need to allow for alternative implementations
authoralanb
Sat, 06 Oct 2012 13:56:16 +0100
changeset 14029 c684694164c2
parent 14028 5f3d5ae5f1ea
child 14030 8dc91f5c3d67
8000354: (props) Properties.storeToXML/loadFromXML need to allow for alternative implementations Reviewed-by: mchung, forax
jdk/make/java/java/FILES_java.gmk
jdk/make/sun/util/Makefile
jdk/src/share/classes/java/util/Properties.java
jdk/src/share/classes/sun/util/spi/XmlPropertiesProvider.java
jdk/src/share/classes/sun/util/xml/META-INF/services/sun.util.spi.XmlPropertiesProvider
jdk/src/share/classes/sun/util/xml/PlatformXmlPropertiesProvider.java
jdk/src/share/classes/sun/util/xml/XMLUtils.java
jdk/test/java/util/Properties/CustomProvider.java
jdk/test/java/util/Properties/LoadAndStoreXML.java
jdk/test/java/util/Properties/MyXmlPropertiesProvider.java
--- a/jdk/make/java/java/FILES_java.gmk	Fri Oct 05 09:57:50 2012 -0700
+++ b/jdk/make/java/java/FILES_java.gmk	Sat Oct 06 13:56:16 2012 +0100
@@ -236,6 +236,7 @@
     java/util/Observer.java \
     java/util/Properties.java \
         java/util/InvalidPropertiesFormatException.java \
+	sun/util/spi/XmlPropertiesProvider.java \
     java/util/PropertyPermission.java \
     java/util/PropertyResourceBundle.java \
     java/util/Random.java \
--- a/jdk/make/sun/util/Makefile	Fri Oct 05 09:57:50 2012 -0700
+++ b/jdk/make/sun/util/Makefile	Sat Oct 06 13:56:16 2012 +0100
@@ -41,3 +41,20 @@
 #
 include $(BUILDDIR)/common/Classes.gmk
 
+#
+# Rules for XML properties provider configuration file
+#
+SERVICEDIR = $(CLASSBINDIR)/META-INF/services
+FILES_copy = $(SERVICEDIR)/sun.util.spi.XmlPropertiesProvider
+
+copy-files: $(FILES_copy)
+
+$(SERVICEDIR)/%: $(SHARE_SRC)/classes/sun/util/xml/META-INF/services/%
+	$(install-file)
+
+build: copy-files
+
+clean::
+	$(RM) $(FILES_copy)
+
+
--- a/jdk/src/share/classes/java/util/Properties.java	Fri Oct 05 09:57:50 2012 -0700
+++ b/jdk/src/share/classes/java/util/Properties.java	Sat Oct 06 13:56:16 2012 +0100
@@ -34,7 +34,10 @@
 import java.io.Writer;
 import java.io.OutputStreamWriter;
 import java.io.BufferedWriter;
-import java.lang.reflect.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import sun.util.spi.XmlPropertiesProvider;
 
 /**
  * The {@code Properties} class represents a persistent set of
@@ -866,7 +869,7 @@
     {
         if (in == null)
             throw new NullPointerException();
-        XMLUtils.load(this, in);
+        XmlSupport.load(this, in);
         in.close();
     }
 
@@ -934,7 +937,7 @@
     {
         if (os == null)
             throw new NullPointerException();
-        XMLUtils.save(this, os, comment, encoding);
+        XmlSupport.save(this, os, comment, encoding);
     }
 
     /**
@@ -1113,59 +1116,82 @@
         '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
     };
 
+    /**
+     * Supporting class for loading/storing properties in XML format.
+     *
+     * <p> The {@code load} and {@code store} methods defined here delegate to a
+     * system-wide {@code XmlPropertiesProvider}. On first invocation of either
+     * method then the system-wide provider is located as follows: </p>
+     *
+     * <ol>
+     *   <li> If the system property {@code sun.util.spi.XmlPropertiesProvider}
+     *   is defined then it is taken to be the full-qualified name of a concrete
+     *   provider class. The class is loaded with the system class loader as the
+     *   initiating loader. If it cannot be loaded or instantiated using a zero
+     *   argument constructor then an unspecified error is thrown. </li>
+     *
+     *   <li> If the system property is not defined then the service-provider
+     *   loading facility defined by the {@link ServiceLoader} class is used to
+     *   locate a provider with the system class loader as the initiating
+     *   loader and {@code sun.util.spi.XmlPropertiesProvider} as the service
+     *   type. If this process fails then an unspecified error is thrown. If
+     *   there is more than one service provider installed then it is
+     *   not specified as to which provider will be used. </li>
+     *
+     *   <li> If the provider is not found by the above means then a system
+     *   default provider will be instantiated and used. </li>
+     * </ol>
+     */
+    private static class XmlSupport {
 
-    private static class XMLUtils {
-        private static Method load = null;
-        private static Method save = null;
-        static {
+        private static XmlPropertiesProvider loadProviderFromProperty(ClassLoader cl) {
+            String cn = System.getProperty("sun.util.spi.XmlPropertiesProvider");
+            if (cn == null)
+                return null;
             try {
-                // reference sun.util.xml.Utils reflectively
-                // to allow the Properties class be compiled in
-                // the absence of XML
-                Class<?> c = Class.forName("sun.util.xml.XMLUtils", true, null);
-                load = c.getMethod("load", Properties.class, InputStream.class);
-                save = c.getMethod("save", Properties.class, OutputStream.class,
-                                   String.class, String.class);
-            } catch (ClassNotFoundException cnf) {
-                throw new AssertionError(cnf);
-            } catch (NoSuchMethodException e) {
-                throw new AssertionError(e);
+                Class<?> c = Class.forName(cn, true, cl);
+                return (XmlPropertiesProvider)c.newInstance();
+            } catch (ClassNotFoundException |
+                     IllegalAccessException |
+                     InstantiationException x) {
+                throw new ServiceConfigurationError(null, x);
             }
         }
 
-        static void invoke(Method m, Object... args) throws IOException {
-            try {
-                m.invoke(null, args);
-            } catch (IllegalAccessException e) {
-                throw new AssertionError(e);
-            } catch (InvocationTargetException e) {
-                Throwable t = e.getCause();
-                if (t instanceof RuntimeException)
-                    throw (RuntimeException)t;
+        private static XmlPropertiesProvider loadProviderAsService(ClassLoader cl) {
+            Iterator<XmlPropertiesProvider> iterator =
+                 ServiceLoader.load(XmlPropertiesProvider.class, cl).iterator();
+            return iterator.hasNext() ? iterator.next() : null;
+        }
 
-                if (t instanceof IOException) {
-                    throw (IOException)t;
-                } else {
-                    throw new AssertionError(t);
-                }
-            }
+        private static XmlPropertiesProvider loadProvider() {
+            return AccessController.doPrivileged(
+                new PrivilegedAction<XmlPropertiesProvider>() {
+                    public XmlPropertiesProvider run() {
+                        ClassLoader cl = ClassLoader.getSystemClassLoader();
+                        XmlPropertiesProvider provider = loadProviderFromProperty(cl);
+                        if (provider != null)
+                            return provider;
+                        provider = loadProviderAsService(cl);
+                        if (provider != null)
+                            return provider;
+                        throw new InternalError("No fallback");
+                }});
         }
 
+        private static final XmlPropertiesProvider PROVIDER = loadProvider();
+
         static void load(Properties props, InputStream in)
             throws IOException, InvalidPropertiesFormatException
         {
-            if (load == null)
-                throw new InternalError("sun.util.xml.XMLUtils not found");
-            invoke(load, props, in);
+            PROVIDER.load(props, in);
         }
 
         static void save(Properties props, OutputStream os, String comment,
                          String encoding)
             throws IOException
         {
-            if (save == null)
-                throw new InternalError("sun.util.xml.XMLUtils not found");
-            invoke(save, props, os, comment, encoding);
+            PROVIDER.store(props, os, comment, encoding);
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/util/spi/XmlPropertiesProvider.java	Sat Oct 06 13:56:16 2012 +0100
@@ -0,0 +1,86 @@
+/*
+ * 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 sun.util.spi;
+
+import java.util.Properties;
+import java.util.InvalidPropertiesFormatException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * Service-provider class for loading and storing {@link Properites} in XML
+ * format.
+ *
+ * @see Properties#loadFromXML
+ * @see Properties#storeToXML
+ */
+
+public abstract class XmlPropertiesProvider {
+
+    /**
+     * Initializes a new instance of this class.
+     */
+    protected XmlPropertiesProvider() {
+        // do nothing for now
+    }
+
+    /**
+     * Loads all of the properties represented by the XML document on the
+     * specified input stream into a properties table.
+     *
+     * @param props the properties table to populate
+     * @param in the input stream from which to read the XML document
+     * @throws IOException if reading from the specified input stream fails
+     * @throws InvalidPropertiesFormatException Data on input stream does not
+     *         constitute a valid XML document with the mandated document type.
+     *
+     * @see Properties#loadFromXML
+     */
+    public abstract void load(Properties props, InputStream in)
+        throws IOException, InvalidPropertiesFormatException;
+
+    /**
+     * Emits an XML document representing all of the properties in a given
+     * table.
+     *
+     * @param props the properies to store
+     * @param out the output stream on which to emit the XML document.
+     * @param comment  a description of the property list, can be @{code null}
+     * @param encoding the name of a supported character encoding
+     *
+     * @throws IOException if writing to the specified output stream fails
+     * @throws NullPointerException if {@code out} is null.
+     * @throws ClassCastException  if this {@code Properties} object
+     *         contains any keys or values that are not
+     *         {@code Strings}.
+     *
+     * @see Properties#storeToXML
+     */
+    public abstract void store(Properties props, OutputStream out,
+                               String comment, String encoding)
+        throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/util/xml/META-INF/services/sun.util.spi.XmlPropertiesProvider	Sat Oct 06 13:56:16 2012 +0100
@@ -0,0 +1,1 @@
+sun.util.xml.PlatformXmlPropertiesProvider
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/util/xml/PlatformXmlPropertiesProvider.java	Sat Oct 06 13:56:16 2012 +0100
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2003, 2011, 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.xml;
+
+import java.io.*;
+import java.util.*;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import javax.xml.parsers.*;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.*;
+import javax.xml.transform.stream.*;
+
+import sun.util.spi.XmlPropertiesProvider;
+
+/**
+ * A {@code XmlPropertiesProvider} implementation that uses the JAXP API
+ * for parsing.
+ *
+ * @author  Michael McCloskey
+ * @since   1.3
+ */
+public class PlatformXmlPropertiesProvider extends XmlPropertiesProvider {
+
+    // XML loading and saving methods for Properties
+
+    // The required DTD URI for exported properties
+    private static final String PROPS_DTD_URI =
+    "http://java.sun.com/dtd/properties.dtd";
+
+    private static final String PROPS_DTD =
+    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+    "<!-- DTD for properties -->"                +
+    "<!ELEMENT properties ( comment?, entry* ) >"+
+    "<!ATTLIST properties"                       +
+        " version CDATA #FIXED \"1.0\">"         +
+    "<!ELEMENT comment (#PCDATA) >"              +
+    "<!ELEMENT entry (#PCDATA) >"                +
+    "<!ATTLIST entry "                           +
+        " key CDATA #REQUIRED>";
+
+    /**
+     * Version number for the format of exported properties files.
+     */
+    private static final String EXTERNAL_XML_VERSION = "1.0";
+
+    @Override
+    public void load(Properties props, InputStream in)
+        throws IOException, InvalidPropertiesFormatException
+    {
+        Document doc = null;
+        try {
+            doc = getLoadingDoc(in);
+        } catch (SAXException saxe) {
+            throw new InvalidPropertiesFormatException(saxe);
+        }
+        Element propertiesElement = doc.getDocumentElement();
+        String xmlVersion = propertiesElement.getAttribute("version");
+        if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0)
+            throw new InvalidPropertiesFormatException(
+                "Exported Properties file format version " + xmlVersion +
+                " is not supported. This java installation can read" +
+                " versions " + EXTERNAL_XML_VERSION + " or older. You" +
+                " may need to install a newer version of JDK.");
+        importProperties(props, propertiesElement);
+    }
+
+    static Document getLoadingDoc(InputStream in)
+        throws SAXException, IOException
+    {
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        dbf.setIgnoringElementContentWhitespace(true);
+        dbf.setValidating(true);
+        dbf.setCoalescing(true);
+        dbf.setIgnoringComments(true);
+        try {
+            DocumentBuilder db = dbf.newDocumentBuilder();
+            db.setEntityResolver(new Resolver());
+            db.setErrorHandler(new EH());
+            InputSource is = new InputSource(in);
+            return db.parse(is);
+        } catch (ParserConfigurationException x) {
+            throw new Error(x);
+        }
+    }
+
+    static void importProperties(Properties props, Element propertiesElement) {
+        NodeList entries = propertiesElement.getChildNodes();
+        int numEntries = entries.getLength();
+        int start = numEntries > 0 &&
+            entries.item(0).getNodeName().equals("comment") ? 1 : 0;
+        for (int i=start; i<numEntries; i++) {
+            Element entry = (Element)entries.item(i);
+            if (entry.hasAttribute("key")) {
+                Node n = entry.getFirstChild();
+                String val = (n == null) ? "" : n.getNodeValue();
+                props.setProperty(entry.getAttribute("key"), val);
+            }
+        }
+    }
+
+    @Override
+    public void store(Properties props, OutputStream os, String comment,
+                      String encoding)
+        throws IOException
+    {
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        DocumentBuilder db = null;
+        try {
+            db = dbf.newDocumentBuilder();
+        } catch (ParserConfigurationException pce) {
+            assert(false);
+        }
+        Document doc = db.newDocument();
+        Element properties =  (Element)
+            doc.appendChild(doc.createElement("properties"));
+
+        if (comment != null) {
+            Element comments = (Element)properties.appendChild(
+                doc.createElement("comment"));
+            comments.appendChild(doc.createTextNode(comment));
+        }
+
+        synchronized (props) {
+            for (String key : props.stringPropertyNames()) {
+                Element entry = (Element)properties.appendChild(
+                    doc.createElement("entry"));
+                entry.setAttribute("key", key);
+                entry.appendChild(doc.createTextNode(props.getProperty(key)));
+            }
+        }
+        emitDocument(doc, os, encoding);
+    }
+
+    static void emitDocument(Document doc, OutputStream os, String encoding)
+        throws IOException
+    {
+        TransformerFactory tf = TransformerFactory.newInstance();
+        Transformer t = null;
+        try {
+            t = tf.newTransformer();
+            t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, PROPS_DTD_URI);
+            t.setOutputProperty(OutputKeys.INDENT, "yes");
+            t.setOutputProperty(OutputKeys.METHOD, "xml");
+            t.setOutputProperty(OutputKeys.ENCODING, encoding);
+        } catch (TransformerConfigurationException tce) {
+            assert(false);
+        }
+        DOMSource doms = new DOMSource(doc);
+        StreamResult sr = new StreamResult(os);
+        try {
+            t.transform(doms, sr);
+        } catch (TransformerException te) {
+            throw new IOException(te);
+        }
+    }
+
+    private static class Resolver implements EntityResolver {
+        public InputSource resolveEntity(String pid, String sid)
+            throws SAXException
+        {
+            if (sid.equals(PROPS_DTD_URI)) {
+                InputSource is;
+                is = new InputSource(new StringReader(PROPS_DTD));
+                is.setSystemId(PROPS_DTD_URI);
+                return is;
+            }
+            throw new SAXException("Invalid system identifier: " + sid);
+        }
+    }
+
+    private static class EH implements ErrorHandler {
+        public void error(SAXParseException x) throws SAXException {
+            throw x;
+        }
+        public void fatalError(SAXParseException x) throws SAXException {
+            throw x;
+        }
+        public void warning(SAXParseException x) throws SAXException {
+            throw x;
+        }
+    }
+
+}
--- a/jdk/src/share/classes/sun/util/xml/XMLUtils.java	Fri Oct 05 09:57:50 2012 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,207 +0,0 @@
-/*
- * Copyright (c) 2003, 2011, 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.xml;
-
-import java.io.*;
-import java.util.*;
-import org.xml.sax.*;
-import org.xml.sax.helpers.*;
-import org.w3c.dom.*;
-import javax.xml.parsers.*;
-import javax.xml.transform.*;
-import javax.xml.transform.dom.*;
-import javax.xml.transform.stream.*;
-
-/**
- * A class used to aid in Properties load and save in XML. Keeping this
- * code outside of Properties helps reduce the number of classes loaded
- * when Properties is loaded.
- *
- * @author  Michael McCloskey
- * @since   1.3
- */
-public class XMLUtils {
-
-    // XML loading and saving methods for Properties
-
-    // The required DTD URI for exported properties
-    private static final String PROPS_DTD_URI =
-    "http://java.sun.com/dtd/properties.dtd";
-
-    private static final String PROPS_DTD =
-    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
-    "<!-- DTD for properties -->"                +
-    "<!ELEMENT properties ( comment?, entry* ) >"+
-    "<!ATTLIST properties"                       +
-        " version CDATA #FIXED \"1.0\">"         +
-    "<!ELEMENT comment (#PCDATA) >"              +
-    "<!ELEMENT entry (#PCDATA) >"                +
-    "<!ATTLIST entry "                           +
-        " key CDATA #REQUIRED>";
-
-    /**
-     * Version number for the format of exported properties files.
-     */
-    private static final String EXTERNAL_XML_VERSION = "1.0";
-
-    public static void load(Properties props, InputStream in)
-        throws IOException, InvalidPropertiesFormatException
-    {
-        Document doc = null;
-        try {
-            doc = getLoadingDoc(in);
-        } catch (SAXException saxe) {
-            throw new InvalidPropertiesFormatException(saxe);
-        }
-        Element propertiesElement = doc.getDocumentElement();
-        String xmlVersion = propertiesElement.getAttribute("version");
-        if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0)
-            throw new InvalidPropertiesFormatException(
-                "Exported Properties file format version " + xmlVersion +
-                " is not supported. This java installation can read" +
-                " versions " + EXTERNAL_XML_VERSION + " or older. You" +
-                " may need to install a newer version of JDK.");
-        importProperties(props, propertiesElement);
-    }
-
-    static Document getLoadingDoc(InputStream in)
-        throws SAXException, IOException
-    {
-        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-        dbf.setIgnoringElementContentWhitespace(true);
-        dbf.setValidating(true);
-        dbf.setCoalescing(true);
-        dbf.setIgnoringComments(true);
-        try {
-            DocumentBuilder db = dbf.newDocumentBuilder();
-            db.setEntityResolver(new Resolver());
-            db.setErrorHandler(new EH());
-            InputSource is = new InputSource(in);
-            return db.parse(is);
-        } catch (ParserConfigurationException x) {
-            throw new Error(x);
-        }
-    }
-
-    static void importProperties(Properties props, Element propertiesElement) {
-        NodeList entries = propertiesElement.getChildNodes();
-        int numEntries = entries.getLength();
-        int start = numEntries > 0 &&
-            entries.item(0).getNodeName().equals("comment") ? 1 : 0;
-        for (int i=start; i<numEntries; i++) {
-            Element entry = (Element)entries.item(i);
-            if (entry.hasAttribute("key")) {
-                Node n = entry.getFirstChild();
-                String val = (n == null) ? "" : n.getNodeValue();
-                props.setProperty(entry.getAttribute("key"), val);
-            }
-        }
-    }
-
-    public static void save(Properties props, OutputStream os, String comment,
-                     String encoding)
-        throws IOException
-    {
-        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-        DocumentBuilder db = null;
-        try {
-            db = dbf.newDocumentBuilder();
-        } catch (ParserConfigurationException pce) {
-            assert(false);
-        }
-        Document doc = db.newDocument();
-        Element properties =  (Element)
-            doc.appendChild(doc.createElement("properties"));
-
-        if (comment != null) {
-            Element comments = (Element)properties.appendChild(
-                doc.createElement("comment"));
-            comments.appendChild(doc.createTextNode(comment));
-        }
-
-        synchronized (props) {
-            for (String key : props.stringPropertyNames()) {
-                Element entry = (Element)properties.appendChild(
-                    doc.createElement("entry"));
-                entry.setAttribute("key", key);
-                entry.appendChild(doc.createTextNode(props.getProperty(key)));
-            }
-        }
-        emitDocument(doc, os, encoding);
-    }
-
-    static void emitDocument(Document doc, OutputStream os, String encoding)
-        throws IOException
-    {
-        TransformerFactory tf = TransformerFactory.newInstance();
-        Transformer t = null;
-        try {
-            t = tf.newTransformer();
-            t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, PROPS_DTD_URI);
-            t.setOutputProperty(OutputKeys.INDENT, "yes");
-            t.setOutputProperty(OutputKeys.METHOD, "xml");
-            t.setOutputProperty(OutputKeys.ENCODING, encoding);
-        } catch (TransformerConfigurationException tce) {
-            assert(false);
-        }
-        DOMSource doms = new DOMSource(doc);
-        StreamResult sr = new StreamResult(os);
-        try {
-            t.transform(doms, sr);
-        } catch (TransformerException te) {
-            IOException ioe = new IOException();
-            ioe.initCause(te);
-            throw ioe;
-        }
-    }
-
-    private static class Resolver implements EntityResolver {
-        public InputSource resolveEntity(String pid, String sid)
-            throws SAXException
-        {
-            if (sid.equals(PROPS_DTD_URI)) {
-                InputSource is;
-                is = new InputSource(new StringReader(PROPS_DTD));
-                is.setSystemId(PROPS_DTD_URI);
-                return is;
-            }
-            throw new SAXException("Invalid system identifier: " + sid);
-        }
-    }
-
-    private static class EH implements ErrorHandler {
-        public void error(SAXParseException x) throws SAXException {
-            throw x;
-        }
-        public void fatalError(SAXParseException x) throws SAXException {
-            throw x;
-        }
-        public void warning(SAXParseException x) throws SAXException {
-            throw x;
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/CustomProvider.java	Sat Oct 06 13:56:16 2012 +0100
@@ -0,0 +1,70 @@
+/*
+ * 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 8000354
+ * @summary Test
+ * @compile -XDignore.symbol.file CustomProvider.java MyXmlPropertiesProvider.java
+ * @run main/othervm -Dsun.util.spi.XmlPropertiesProvider=MyXmlPropertiesProvider CustomProvider
+ */
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * Sanity test to verify that the property sun.util.spi.XmlPropertiesProvider
+ * can be used to specify a custom provider for loading/storing properties
+ * in XML format.
+ */
+public class CustomProvider {
+
+    public static void main(String[] args) throws IOException {
+        String provider = System.getProperty("sun.util.spi.XmlPropertiesProvider");
+        assertTrue(provider != null, "sun.util.spi.XmlPropertiesProvider not set");
+
+        OutputStream out = new ByteArrayOutputStream();
+        InputStream in = new ByteArrayInputStream(new byte[100]);
+
+        Properties props;
+
+        props = new Properties();
+        props.loadFromXML(in);
+
+        props = System.getProperties();
+        props.storeToXML(out, "comment");
+        props.storeToXML(out, "comment", "UTF-8");
+
+        // check that the provider's load and store methods have been invoked
+
+        assertTrue(MyXmlPropertiesProvider.createCount() == 1,
+            "Provider should only be created once");
+        assertTrue(MyXmlPropertiesProvider.loadCount() == 1,
+            "load method expected to be called once");
+        assertTrue(MyXmlPropertiesProvider.storeCount() == 2,
+             "store method expected to be called twice");
+    }
+
+    static void assertTrue(boolean b, String msg) {
+        if (!b) throw new RuntimeException(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/LoadAndStoreXML.java	Sat Oct 06 13:56:16 2012 +0100
@@ -0,0 +1,108 @@
+/*
+ * 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 8000354
+ * @summary Basic test of storeToXML and loadToXML
+ */
+
+import java.io.*;
+import java.util.*;
+import java.security.*;
+
+public class LoadAndStoreXML {
+
+    /**
+     * Simple policy implementation that grants a set of permissions to
+     * all code sources and protection domains.
+     */
+    static class SimplePolicy extends Policy {
+        private final Permissions perms;
+
+        public SimplePolicy(Permission...permissions) {
+            perms = new Permissions();
+            for (Permission permission : permissions)
+                perms.add(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource cs) {
+            return perms;
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain pd) {
+            return perms;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain pd, Permission p) {
+            return perms.implies(p);
+        }
+    }
+
+    /**
+     * Sanity test that properties saved with Properties#storeToXML can be
+     * read with Properties#loadFromXML.
+     */
+    static void test() throws IOException {
+        Properties props = new Properties();
+        props.put("k1", "foo");
+        props.put("k2", "bar");
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        props.storeToXML(out, "no comment");
+
+        Properties p = new Properties();
+        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+        p.loadFromXML(in);
+
+        if (!p.equals(props)) {
+            System.err.println("stored: " + props);
+            System.err.println("loaded: " + p);
+            throw new RuntimeException("Test failed");
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+
+        // run test without security manager
+        test();
+
+        // re-run test with security manager
+        Policy orig = Policy.getPolicy();
+        Policy p = new SimplePolicy(new RuntimePermission("setSecurityManager"),
+                                    new PropertyPermission("line.separator", "read"));
+        Policy.setPolicy(p);
+        System.setSecurityManager(new SecurityManager());
+        try {
+            test();
+        } finally {
+            // turn off security manager and restore policy
+            System.setSecurityManager(null);
+            Policy.setPolicy(orig);
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/Properties/MyXmlPropertiesProvider.java	Sat Oct 06 13:56:16 2012 +0100
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+import java.util.*;
+import java.io.*;
+
+public class MyXmlPropertiesProvider
+    extends sun.util.spi.XmlPropertiesProvider
+{
+    private static int createCount;
+    private static int loadCount;
+    private static int storeCount;
+
+    static int createCount() { return createCount; }
+    static int loadCount() { return loadCount; }
+    static int storeCount() { return storeCount; }
+
+    public MyXmlPropertiesProvider() {
+        createCount++;
+    }
+
+    @Override
+    public void load(Properties props, InputStream in)
+        throws IOException, InvalidPropertiesFormatException
+    {
+        loadCount++;
+    }
+
+    @Override
+    public void store(Properties props, OutputStream out,
+                      String comment, String encoding)
+        throws IOException
+    {
+        storeCount++;
+    }
+}