1 /*
2  * Copyright (c) 2018, 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 /* @test
25  * @bug 8185582 8197989
26  * @modules java.base/java.util.zip:open java.base/jdk.internal.vm.annotation
27  * @summary Check the resources of Inflater, Deflater and ZipFile are always
28  *          cleaned/released when the instance is not unreachable
29  */
30 
31 import java.io.*;
32 import java.lang.reflect.*;
33 import java.util.*;
34 import java.util.zip.*;
35 import jdk.internal.vm.annotation.DontInline;
36 import static java.nio.charset.StandardCharsets.US_ASCII;
37 
38 public class TestCleaner {
39 
main(String[] args)40     public static void main(String[] args) throws Throwable {
41         testDeInflater();
42         testZipFile();
43     }
44 
addrOf(Object obj)45     private static long addrOf(Object obj) {
46         try {
47             Field addr = obj.getClass().getDeclaredField("address");
48             if (!addr.trySetAccessible()) {
49                 return -1;
50             }
51             return addr.getLong(obj);
52         } catch (Exception x) {
53             return -1;
54         }
55     }
56 
57     // verify the "native resource" of In/Deflater has been cleaned
testDeInflater()58     private static void testDeInflater() throws Throwable {
59         Field zsRefDef = Deflater.class.getDeclaredField("zsRef");
60         Field zsRefInf = Inflater.class.getDeclaredField("zsRef");
61         if (!zsRefDef.trySetAccessible() || !zsRefInf.trySetAccessible()) {
62             throw new RuntimeException("'zsRef' is not accesible");
63         }
64         if (addrOf(zsRefDef.get(new Deflater())) == -1 ||
65             addrOf(zsRefInf.get(new Inflater())) == -1) {
66             throw new RuntimeException("'addr' is not accesible");
67         }
68         List<Object> list = new ArrayList<>();
69         byte[] buf1 = new byte[1024];
70         byte[] buf2 = new byte[1024];
71         for (int i = 0; i < 10; i++) {
72             var def = new Deflater();
73             list.add(zsRefDef.get(def));
74             def.setInput("hello".getBytes());
75             def.finish();
76             int n = def.deflate(buf1);
77 
78             var inf = new Inflater();
79             list.add(zsRefInf.get(inf));
80             inf.setInput(buf1, 0, n);
81             n = inf.inflate(buf2);
82             if (!"hello".equals(new String(buf2, 0, n))) {
83                 throw new RuntimeException("compression/decompression failed");
84             }
85         }
86 
87         int n = 10;
88         long cnt = list.size();
89         while (n-- > 0 && cnt != 0) {
90             Thread.sleep(100);
91             System.gc();
92             cnt = list.stream().filter(o -> addrOf(o) != 0).count();
93         }
94         if (cnt != 0)
95             throw new RuntimeException("cleaner failed to clean : " + cnt);
96 
97     }
98 
99     @DontInline
openAndCloseZipFile(File zip)100     private static Object openAndCloseZipFile(File zip) throws Throwable {
101         try {
102             try (var fos = new FileOutputStream(zip);
103                  var zos = new ZipOutputStream(fos)) {
104                 zos.putNextEntry(new ZipEntry("hello"));
105                 zos.write("hello".getBytes(US_ASCII));
106                 zos.closeEntry();
107             }
108 
109             var zf = new ZipFile(zip);
110             var es = zf.entries();
111             while (es.hasMoreElements()) {
112                 zf.getInputStream(es.nextElement()).read();
113             }
114 
115             Field fieldRes = ZipFile.class.getDeclaredField("res");
116             if (!fieldRes.trySetAccessible()) {
117                 throw new RuntimeException("'ZipFile.res' is not accesible");
118             }
119             Object zfRes = fieldRes.get(zf);
120             if (zfRes == null) {
121                 throw new RuntimeException("'ZipFile.res' is null");
122             }
123             Field fieldZsrc = zfRes.getClass().getDeclaredField("zsrc");
124             if (!fieldZsrc.trySetAccessible()) {
125                 throw new RuntimeException("'ZipFile.zsrc' is not accesible");
126             }
127             return fieldZsrc.get(zfRes);
128         } finally {
129             zip.delete();
130         }
131     }
132 
133 
testZipFile()134     private static void testZipFile() throws Throwable {
135         File dir = new File(System.getProperty("test.dir", "."));
136         File zip = File.createTempFile("testzf", "zip", dir);
137 
138         Object zsrc = openAndCloseZipFile(zip);
139         if (zsrc != null) {
140             Field zfileField = zsrc.getClass().getDeclaredField("zfile");
141             if (!zfileField.trySetAccessible()) {
142                 throw new RuntimeException("'ZipFile.Source.zfile' is not accesible");
143             }
144             //System.out.println("zffile: " +  zfileField.get(zsrc));
145             int n = 10;
146             while (n-- > 0 && zfileField.get(zsrc) != null) {
147                 System.out.println("waiting gc ... " + n);
148                 System.gc();
149                 Thread.sleep(100);
150             }
151             if (zfileField.get(zsrc) != null) {
152                 throw new RuntimeException("cleaner failed to clean zipfile.");
153             }
154         }
155     }
156 }
157