|
1 /* |
|
2 * Copyright (c) 2014, 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 8047769 |
|
27 * @summary SecureRandom should be more frugal with file descriptors |
|
28 */ |
|
29 |
|
30 import java.io.File; |
|
31 import java.io.FileOutputStream; |
|
32 import java.io.IOException; |
|
33 import java.io.InputStream; |
|
34 import java.lang.ref.Reference; |
|
35 import java.lang.reflect.InvocationTargetException; |
|
36 import java.lang.reflect.Method; |
|
37 import java.lang.reflect.UndeclaredThrowableException; |
|
38 import java.util.Arrays; |
|
39 |
|
40 public class FileInputStreamPoolTest { |
|
41 |
|
42 static final byte[] bytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8}; |
|
43 |
|
44 static void testCaching(File file) throws IOException { |
|
45 InputStream in1 = TestProxy.FileInputStreamPool_getInputStream(file); |
|
46 InputStream in2 = TestProxy.FileInputStreamPool_getInputStream(file); |
|
47 assertTrue(in1 == in2, |
|
48 "1st InputStream: " + in1 + |
|
49 " is not same as 2nd: " + in2); |
|
50 |
|
51 byte[] readBytes = new byte[bytes.length]; |
|
52 int nread = in1.read(readBytes); |
|
53 assertTrue(bytes.length == nread, |
|
54 "short read: " + nread + |
|
55 " bytes of expected: " + bytes.length); |
|
56 assertTrue(Arrays.equals(readBytes, bytes), |
|
57 "readBytes: " + Arrays.toString(readBytes) + |
|
58 " not equal to expected: " + Arrays.toString(bytes)); |
|
59 } |
|
60 |
|
61 static void assertTrue(boolean test, String message) { |
|
62 if (!test) { |
|
63 throw new AssertionError(message); |
|
64 } |
|
65 } |
|
66 |
|
67 static void processReferences() { |
|
68 // make JVM process References |
|
69 System.gc(); |
|
70 // help ReferenceHandler thread enqueue References |
|
71 while (TestProxy.Reference_tryHandlePending(false)) {} |
|
72 // help run Finalizers |
|
73 System.runFinalization(); |
|
74 } |
|
75 |
|
76 public static void main(String[] args) throws Exception { |
|
77 // 1st create temporary file |
|
78 File file = File.createTempFile("test", ".dat"); |
|
79 try (AutoCloseable acf = () -> { |
|
80 // On Windows, failure to delete file is probably a consequence |
|
81 // of the file still being opened - so the test should fail. |
|
82 assertTrue(file.delete(), |
|
83 "Can't delete: " + file + " (is it still open?)"); |
|
84 }) { |
|
85 try (FileOutputStream out = new FileOutputStream(file)) { |
|
86 out.write(bytes); |
|
87 } |
|
88 |
|
89 // test caching 1t time |
|
90 testCaching(file); |
|
91 |
|
92 processReferences(); |
|
93 |
|
94 // test caching 2nd time - this should only succeed if the stream |
|
95 // is re-opened as a consequence of cleared WeakReference |
|
96 testCaching(file); |
|
97 |
|
98 processReferences(); |
|
99 } |
|
100 } |
|
101 |
|
102 /** |
|
103 * A proxy for (package)private static methods: |
|
104 * sun.security.provider.FileInputStreamPool.getInputStream |
|
105 * java.lang.ref.Reference.tryHandlePending |
|
106 */ |
|
107 static class TestProxy { |
|
108 private static final Method getInputStreamMethod; |
|
109 private static final Method tryHandlePendingMethod; |
|
110 |
|
111 static { |
|
112 try { |
|
113 Class<?> fileInputStreamPoolClass = |
|
114 Class.forName("sun.security.provider.FileInputStreamPool"); |
|
115 getInputStreamMethod = |
|
116 fileInputStreamPoolClass.getDeclaredMethod( |
|
117 "getInputStream", File.class); |
|
118 getInputStreamMethod.setAccessible(true); |
|
119 |
|
120 tryHandlePendingMethod = Reference.class.getDeclaredMethod( |
|
121 "tryHandlePending", boolean.class); |
|
122 tryHandlePendingMethod.setAccessible(true); |
|
123 } catch (Exception e) { |
|
124 throw new Error(e); |
|
125 } |
|
126 } |
|
127 |
|
128 static InputStream FileInputStreamPool_getInputStream(File file) |
|
129 throws IOException { |
|
130 try { |
|
131 return (InputStream) getInputStreamMethod.invoke(null, file); |
|
132 } catch (InvocationTargetException e) { |
|
133 Throwable te = e.getTargetException(); |
|
134 if (te instanceof IOException) { |
|
135 throw (IOException) te; |
|
136 } else if (te instanceof RuntimeException) { |
|
137 throw (RuntimeException) te; |
|
138 } else if (te instanceof Error) { |
|
139 throw (Error) te; |
|
140 } else { |
|
141 throw new UndeclaredThrowableException(te); |
|
142 } |
|
143 } catch (IllegalAccessException e) { |
|
144 throw new RuntimeException(e); |
|
145 } |
|
146 } |
|
147 |
|
148 static boolean Reference_tryHandlePending(boolean waitForNotify) { |
|
149 try { |
|
150 return (boolean) tryHandlePendingMethod |
|
151 .invoke(null, waitForNotify); |
|
152 } catch (InvocationTargetException e) { |
|
153 Throwable te = e.getTargetException(); |
|
154 if (te instanceof RuntimeException) { |
|
155 throw (RuntimeException) te; |
|
156 } else if (te instanceof Error) { |
|
157 throw (Error) te; |
|
158 } else { |
|
159 throw new UndeclaredThrowableException(te); |
|
160 } |
|
161 } catch (IllegalAccessException e) { |
|
162 throw new RuntimeException(e); |
|
163 } |
|
164 } |
|
165 } |
|
166 } |