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