1 /*
2  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 8000354 8000685 8004371 8043119
27  * @summary Basic test of storeToXML and loadToXML
28  * @run main/othervm -Djava.security.manager=allow LoadAndStoreXML
29  */
30 
31 package test.java.util.Properties;
32 
33 import java.io.ByteArrayInputStream;
34 import java.io.ByteArrayOutputStream;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.UnsupportedEncodingException;
38 import java.nio.charset.Charset;
39 import java.nio.file.DirectoryStream;
40 import java.nio.file.Files;
41 import java.nio.file.Path;
42 import java.nio.file.Paths;
43 import java.security.CodeSource;
44 import java.security.Permission;
45 import java.security.PermissionCollection;
46 import java.security.Permissions;
47 import java.security.Policy;
48 import java.security.ProtectionDomain;
49 import java.util.InvalidPropertiesFormatException;
50 import java.util.Properties;
51 import java.util.PropertyPermission;
52 import java.util.Set;
53 
54 public class LoadAndStoreXML {
55     static final String bomChar = "\uFEFF";
56 
57     /**
58      * Simple policy implementation that grants a set of permissions to
59      * all code sources and protection domains.
60      */
61     static class SimplePolicy extends Policy {
62         private final Permissions perms;
63 
SimplePolicy(Permission...permissions)64         public SimplePolicy(Permission...permissions) {
65             perms = new Permissions();
66             for (Permission permission : permissions)
67                 perms.add(permission);
68         }
69 
70         @Override
getPermissions(CodeSource cs)71         public PermissionCollection getPermissions(CodeSource cs) {
72             return perms;
73         }
74 
75         @Override
getPermissions(ProtectionDomain pd)76         public PermissionCollection getPermissions(ProtectionDomain pd) {
77             return perms;
78         }
79 
80         @Override
implies(ProtectionDomain pd, Permission p)81         public boolean implies(ProtectionDomain pd, Permission p) {
82             return perms.implies(p);
83         }
84     }
85 
86     /**
87      * A {@code ByteArrayInputStream} that allows testing if the
88      * {@code close} method has been invoked.
89      */
90     static class TestInputStream extends ByteArrayInputStream {
91         private boolean closed;
92 
TestInputStream(byte[] buf)93         TestInputStream(byte[] buf) {
94             super(buf);
95         }
96 
isOpen()97         boolean isOpen() {
98             return !closed;
99         }
100 
close()101         public void close() throws IOException {
102             try {
103                 super.close();
104             } finally {
105                 closed = true;
106             }
107         }
108     }
109 
110     /**
111      * A {@code ByteArrayOutputStream} that allows testing if the
112      * {@code close} method has been invoked.
113      */
114     static class TestOutputStream extends ByteArrayOutputStream {
115         private boolean closed;
116 
isOpen()117         boolean isOpen() {
118             return !closed;
119         }
120 
close()121         public void close() throws IOException {
122             try {
123                 super.close();
124             } finally {
125                 closed = true;
126             }
127         }
128     }
129 
130     /**
131      * Sanity test that properties saved with Properties#storeToXML can be
132      * read with Properties#loadFromXML.
133      */
testLoadAndStore(String encoding, boolean appendBOM)134     static void testLoadAndStore(String encoding, boolean appendBOM) throws IOException {
135         System.out.println("testLoadAndStore, encoding=" + encoding);
136 
137         Properties props = new Properties();
138         props.put("k0", "\u6C34");
139         props.put("k1", "foo");
140         props.put("k2", "bar");
141         props.put("k3", "\u0020\u0391\u0392\u0393\u0394\u0395\u0396\u0397");
142         props.put("k4", "\u7532\u9aa8\u6587");
143         props.put("k5", "<java.home>/conf/jaxp.properties");
144 
145         TestOutputStream out = new TestOutputStream();
146         props.storeToXML(out, null, encoding);
147         if (!out.isOpen())
148             throw new RuntimeException("OutputStream closed by storeToXML");
149 
150         Properties p = new Properties();
151         TestInputStream in;
152         if (appendBOM) {
153             byte[] byteOrderMark = bomChar.getBytes(Charset.forName(encoding));
154             byte[] outArray = out.toByteArray();
155             byte[] inputArray = new byte[byteOrderMark.length + outArray.length];
156             System.arraycopy(byteOrderMark, 0, inputArray, 0, byteOrderMark.length);
157             System.arraycopy(outArray, 0, inputArray, byteOrderMark.length, outArray.length);
158             in = new TestInputStream(inputArray);
159         } else {
160             in = new TestInputStream(out.toByteArray());
161         }
162         p.loadFromXML(in);
163         if (in.isOpen())
164             throw new RuntimeException("InputStream not closed by loadFromXML");
165 
166         if (!p.equals(props)) {
167             System.err.println("stored: " + props);
168             System.err.println("loaded: " + p);
169             throw new RuntimeException("Test failed");
170         }
171     }
172 
173     /**
174      * Test loadFromXML with a document that does not have an encoding declaration
175      */
testLoadWithoutEncoding()176     static void testLoadWithoutEncoding() throws IOException {
177         System.out.println("testLoadWithoutEncoding");
178 
179         Properties expected = new Properties();
180         expected.put("foo", "bar");
181 
182         String s = "<?xml version=\"1.0\"?>" +
183                    "<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">" +
184                    "<properties>" +
185                    "<entry key=\"foo\">bar</entry>" +
186                    "</properties>";
187         ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes("UTF-8"));
188         Properties props = new Properties();
189         props.loadFromXML(in);
190 
191         if (!props.equals(expected)) {
192             System.err.println("loaded: " + props + ", expected: " + expected);
193             throw new RuntimeException("Test failed");
194         }
195     }
196 
197     /**
198      * Test loadFromXML with unsupported encoding
199      */
testLoadWithBadEncoding()200     static void testLoadWithBadEncoding() throws IOException {
201         System.out.println("testLoadWithBadEncoding");
202         String s = "<?xml version=\"1.0\" encoding=\"BAD\"?>" +
203                    "<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">" +
204                    "<properties>" +
205                    "<entry key=\"foo\">bar</entry>" +
206                    "</properties>";
207         ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes("UTF-8"));
208         Properties props = new Properties();
209         try {
210             props.loadFromXML(in);
211             throw new RuntimeException("UnsupportedEncodingException expected");
212         } catch (UnsupportedEncodingException expected) { }
213     }
214 
215     /**
216      * Test storeToXML with unsupported encoding
217      */
testStoreWithBadEncoding()218     static void testStoreWithBadEncoding() throws IOException {
219         System.out.println("testStoreWithBadEncoding");
220         Properties props = new Properties();
221         props.put("foo", "bar");
222         ByteArrayOutputStream out = new ByteArrayOutputStream();
223         try {
224             props.storeToXML(out, null, "BAD");
225             throw new RuntimeException("UnsupportedEncodingException expected");
226         } catch (UnsupportedEncodingException expected) { }
227     }
228 
229     /**
230      * Test loadFromXML with malformed documents
231      */
testLoadWithMalformedDoc(Path dir)232     static void testLoadWithMalformedDoc(Path dir) throws IOException {
233         try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.xml")) {
234             for (Path file: stream) {
235                 System.out.println("testLoadWithMalformedDoc, file=" + file.getFileName());
236                 try (InputStream in = Files.newInputStream(file)) {
237                     Properties props = new Properties();
238                     try {
239                         props.loadFromXML(in);
240                         throw new RuntimeException("InvalidPropertiesFormatException not thrown");
241                     } catch (InvalidPropertiesFormatException x) {
242                         System.out.println(x);
243                     }
244                 }
245             }
246         }
247     }
248 
main(String[] args)249     public static void main(String[] args) throws IOException {
250 
251         testLoadAndStore("UTF-8", false);
252         testLoadAndStore("UTF-16", false);
253         testLoadAndStore("UTF-16BE", false);
254         testLoadAndStore("UTF-16LE", false);
255         // Android-removed: current implementation fails to read inputs with BOM.
256         // testLoadAndStore("UTF-16BE", true);
257         // testLoadAndStore("UTF-16LE", true);
258         testLoadWithoutEncoding();
259         // Android-removed: current implementation does not throw.
260         // testLoadWithBadEncoding();
261         // testStoreWithBadEncoding();
262 
263         // malformed documents
264         // Android-removed: current implementation does not throw.
265         // String src = System.getProperty("test.src");
266         // String subdir = "invalidxml";
267         // Path dir = (src == null) ? Paths.get(subdir) : Paths.get(src, subdir);
268         // testLoadWithMalformedDoc(dir);
269 
270         // re-run sanity test with security manager
271         // Android-removed: there is no security manager on Android.
272         /*
273         Policy orig = Policy.getPolicy();
274         Policy p = new SimplePolicy(new RuntimePermission("setSecurityManager"),
275                                     new PropertyPermission("line.separator", "read"));
276         Policy.setPolicy(p);
277         System.setSecurityManager(new SecurityManager());
278         try {
279             testLoadAndStore("UTF-8", false);
280         } finally {
281             // turn off security manager and restore policy
282             System.setSecurityManager(null);
283             Policy.setPolicy(orig);
284         }
285 
286         */
287     }
288 }
289