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 /*
  25  * @test
  26  * @bug 8164512
  27  * @summary verify if the native library is unloaded when the class loader is GC'ed
  28  * @build q.Bridge p.Test
  29  * @run main/othervm/native -Xcheck:jni NativeLibraryTest
  30  */
  31 
  32 import java.net.MalformedURLException;
  33 import java.net.URL;
  34 import java.net.URLClassLoader;
  35 import java.nio.file.Files;
  36 import java.nio.file.Path;
  37 import java.nio.file.Paths;
  38 import q.Bridge;
  39 
  40 public class NativeLibraryTest {
  41     static final Path CLASSES = Paths.get("classes");
  42     static int unloaded = 0;
  43 
  44     public static void main(String... args) throws Exception {
  45         String dir = System.getProperty("test.classes", ".");
  46         Path file = Paths.get("p", "Test.class");
  47         Files.createDirectories(CLASSES.resolve("p"));
  48         Files.move(Paths.get(dir).resolve(file),
  49                    CLASSES.resolve("p").resolve("Test.class"));
  50 
  51         for (int count=1; count <= 5; count++) {
  52             reload();
  53             System.gc();
  54             // give Cleaner thread a chance to unload the native library
  55             Thread.sleep(100);
  56 
  57             if (count != unloaded) {
  58                 throw new RuntimeException("Expected unloaded=" + count +
  59                     " but got=" + unloaded);
  60             }
  61         }
  62     }
  63 
  64     /*
  65      * Loads p.Test class with a new class loader and its static initializer
  66      * will load a native library.
  67      *
  68      * The class loader becomes unreachable when this method returns and
  69      * the native library should be unloaded at some point after the class
  70      * loader is garbage collected.
  71      */
  72     static void reload() throws Exception {
  73         TestLoader loader = new TestLoader();
  74         Class<?> c = Class.forName("p.Test", true, loader);
  75         Bridge b = Bridge.class.cast(c.newInstance());
  76         if (!b.isLoaded()) {
  77             throw new RuntimeException("native library not loaded");
  78         }
  79     }
  80 
  81     static class TestLoader extends URLClassLoader {
  82         static URL[] toURLs() {
  83             try {
  84                 return new URL[] { CLASSES.toUri().toURL() };
  85             } catch (MalformedURLException e) {
  86                 throw new Error(e);
  87             }
  88         }
  89 
  90         TestLoader() {
  91             super("testloader", toURLs(), ClassLoader.getSystemClassLoader());
  92         }
  93     }
  94 
  95     /*
  96      * Called by JNI_OnUnload when the native library is unloaded
  97      */
  98     static void unload() {
  99         unloaded++;
 100     }
 101 }