8240975: Extend NativeLibraries to support explicit unloading
Reviewed-by: alanb, mcimadamore
This commit is contained in:
parent
b66c680885
commit
75a8b7fa83
src/java.base/share
classes
native/libjava
test/jdk/jdk/internal/loader/NativeLibraries
@ -2374,7 +2374,7 @@ public abstract class ClassLoader {
|
||||
return null;
|
||||
}
|
||||
|
||||
private final NativeLibraries libraries = new NativeLibraries(this);
|
||||
private final NativeLibraries libraries = NativeLibraries.jniNativeLibraries(this);
|
||||
|
||||
// Invoked in the java.lang.Runtime class to implement load and loadLibrary.
|
||||
static NativeLibrary loadLibrary(Class<?> fromClass, File file) {
|
||||
|
@ -73,7 +73,7 @@ public class BootLoader {
|
||||
|
||||
// native libraries loaded by the boot class loader
|
||||
private static final NativeLibraries NATIVE_LIBS
|
||||
= new NativeLibraries(null);
|
||||
= NativeLibraries.jniNativeLibraries(null);
|
||||
|
||||
/**
|
||||
* Returns the unnamed module for the boot loader.
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
package jdk.internal.loader;
|
||||
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
import jdk.internal.util.StaticProperty;
|
||||
|
||||
@ -34,6 +35,7 @@ import java.security.PrivilegedAction;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -52,24 +54,73 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
public final class NativeLibraries {
|
||||
|
||||
private final Map<String, NativeLibrary> libraries = new ConcurrentHashMap<>();
|
||||
private final Map<String, NativeLibraryImpl> libraries = new ConcurrentHashMap<>();
|
||||
private final ClassLoader loader;
|
||||
private final Class<?> caller; // may be null. If not null, this is used as
|
||||
// fromClass as a fast-path. See loadLibrary(String name).
|
||||
// caller, if non-null, is the fromClass parameter for NativeLibraries::loadLibrary
|
||||
// unless specified
|
||||
private final Class<?> caller; // may be null
|
||||
private final boolean searchJavaLibraryPath;
|
||||
// loading JNI native libraries
|
||||
private final boolean isJNI;
|
||||
|
||||
public NativeLibraries(ClassLoader loader) {
|
||||
/**
|
||||
* Creates a NativeLibraries instance for loading JNI native libraries
|
||||
* via for System::loadLibrary use.
|
||||
*
|
||||
* 1. Support of auto-unloading. The loaded native libraries are unloaded
|
||||
* when the class loader is reclaimed.
|
||||
* 2. Support of linking of native method. See JNI spec.
|
||||
* 3. Restriction on a native library that can only be loaded by one class loader.
|
||||
* Each class loader manages its own set of native libraries.
|
||||
* The same JNI native library cannot be loaded into more than one class loader.
|
||||
*
|
||||
* This static factory method is intended only for System::loadLibrary use.
|
||||
*
|
||||
* @see <a href="${docroot}/specs/jni/invocation.html##library-and-version-management">
|
||||
* JNI Specification: Library and Version Management</a>
|
||||
*/
|
||||
public static NativeLibraries jniNativeLibraries(ClassLoader loader) {
|
||||
return new NativeLibraries(loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a raw NativeLibraries instance that has the following properties:
|
||||
* 1. Native libraries loaded in this raw NativeLibraries instance are
|
||||
* not JNI native libraries. Hence JNI_OnLoad and JNI_OnUnload will
|
||||
* be ignored. No support for linking of native method.
|
||||
* 2. Native libraries not auto-unloaded. They may be explicitly unloaded
|
||||
* via NativeLibraries::unload.
|
||||
* 3. No relationship with class loaders.
|
||||
*
|
||||
* This static factory method is restricted for JDK trusted class use.
|
||||
*/
|
||||
public static NativeLibraries rawNativeLibraries(Class<?> trustedCaller,
|
||||
boolean searchJavaLibraryPath) {
|
||||
return new NativeLibraries(trustedCaller, searchJavaLibraryPath);
|
||||
}
|
||||
|
||||
private NativeLibraries(ClassLoader loader) {
|
||||
// for null loader, default the caller to this class and
|
||||
// do not search java.library.path
|
||||
this(loader, loader != null ? null : NativeLibraries.class, loader != null ? true : false);
|
||||
}
|
||||
public NativeLibraries(ClassLoader loader, Class<?> caller, boolean searchJavaLibraryPath) {
|
||||
if (caller != null && caller.getClassLoader() != loader) {
|
||||
throw new IllegalArgumentException(caller.getName() + " must be defined by " + loader);
|
||||
}
|
||||
this.loader = loader;
|
||||
this.caller = loader != null ? null : NativeLibraries.class;
|
||||
this.searchJavaLibraryPath = loader != null ? true : false;
|
||||
this.isJNI = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs a NativeLibraries instance of no relationship with class loaders
|
||||
* and disabled auto unloading.
|
||||
*/
|
||||
private NativeLibraries(Class<?> caller, boolean searchJavaLibraryPath) {
|
||||
Objects.requireNonNull(caller);
|
||||
if (!VM.isSystemDomainLoader(caller.getClassLoader())) {
|
||||
throw new IllegalArgumentException("must be JDK trusted class");
|
||||
}
|
||||
this.loader = caller.getClassLoader();
|
||||
this.caller = caller;
|
||||
this.searchJavaLibraryPath = searchJavaLibraryPath;
|
||||
this.isJNI = false;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -169,11 +220,26 @@ public final class NativeLibraries {
|
||||
}
|
||||
}
|
||||
|
||||
NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin);
|
||||
NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin, isJNI);
|
||||
// load the native library
|
||||
nativeLibraryContext.push(lib);
|
||||
try {
|
||||
if (!lib.open()) return null;
|
||||
if (!lib.open()) {
|
||||
return null; // fail to open the native library
|
||||
}
|
||||
// auto unloading is only supported for JNI native libraries
|
||||
// loaded by custom class loaders that can be unloaded.
|
||||
// built-in class loaders are never unloaded.
|
||||
boolean autoUnload = isJNI && !VM.isSystemDomainLoader(loader)
|
||||
&& loader != ClassLoaders.appClassLoader();
|
||||
if (autoUnload) {
|
||||
// register the loaded native library for auto unloading
|
||||
// when the class loader is reclaimed, all native libraries
|
||||
// loaded that class loader will be unloaded.
|
||||
// The entries in the libraries map are not removed since
|
||||
// the entire map will be reclaimed altogether.
|
||||
CleanerFactory.cleaner().register(loader, lib.unloader());
|
||||
}
|
||||
} finally {
|
||||
nativeLibraryContext.pop();
|
||||
}
|
||||
@ -218,6 +284,26 @@ public final class NativeLibraries {
|
||||
return lib;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads the given native library
|
||||
*
|
||||
* @param lib native library
|
||||
*/
|
||||
public void unload(NativeLibrary lib) {
|
||||
if (isJNI) {
|
||||
throw new UnsupportedOperationException("explicit unloading cannot be used with auto unloading");
|
||||
}
|
||||
Objects.requireNonNull(lib);
|
||||
synchronized (loadedLibraryNames) {
|
||||
NativeLibraryImpl nl = libraries.remove(lib.name());
|
||||
if (nl != lib) {
|
||||
throw new IllegalArgumentException(lib.name() + " not loaded by this NativeLibraries instance");
|
||||
}
|
||||
// unload the native library and also remove from the global name registry
|
||||
nl.unloader().run();
|
||||
}
|
||||
}
|
||||
|
||||
private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
|
||||
for (String path : paths) {
|
||||
File libfile = new File(path, System.mapLibraryName(name));
|
||||
@ -255,16 +341,21 @@ public final class NativeLibraries {
|
||||
final String name;
|
||||
// Indicates if the native library is linked into the VM
|
||||
final boolean isBuiltin;
|
||||
// Indicate if this is JNI native library
|
||||
final boolean isJNI;
|
||||
|
||||
// opaque handle to native library, used in native code.
|
||||
long handle;
|
||||
// the version of JNI environment the native library requires.
|
||||
int jniVersion;
|
||||
|
||||
NativeLibraryImpl(Class<?> fromClass, String name, boolean isBuiltin) {
|
||||
NativeLibraryImpl(Class<?> fromClass, String name, boolean isBuiltin, boolean isJNI) {
|
||||
assert !isBuiltin || isJNI : "a builtin native library must be JNI library";
|
||||
|
||||
this.fromClass = fromClass;
|
||||
this.name = name;
|
||||
this.isBuiltin = isBuiltin;
|
||||
this.isJNI = isJNI;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -277,26 +368,19 @@ public final class NativeLibraries {
|
||||
return findEntry0(this, name);
|
||||
}
|
||||
|
||||
Runnable unloader() {
|
||||
return new Unloader(name, handle, isBuiltin, isJNI);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads the native library and registers for cleanup when its
|
||||
* associated class loader is unloaded
|
||||
* Loads the named native library
|
||||
*/
|
||||
boolean open() {
|
||||
if (handle != 0) {
|
||||
throw new InternalError("Native library " + name + " has been loaded");
|
||||
}
|
||||
|
||||
if (!load(this, name, isBuiltin)) return false;
|
||||
|
||||
// register the class loader for cleanup when unloaded
|
||||
// builtin class loaders are never unloaded
|
||||
ClassLoader loader = fromClass != null ? fromClass.getClassLoader() : null;
|
||||
if (loader != null &&
|
||||
loader != ClassLoaders.platformClassLoader() &&
|
||||
loader != ClassLoaders.appClassLoader()) {
|
||||
CleanerFactory.cleaner().register(loader, new Unloader(name, handle, isBuiltin));
|
||||
}
|
||||
return true;
|
||||
return load(this, name, isBuiltin, isJNI);
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,13 +392,15 @@ public final class NativeLibraries {
|
||||
// This represents the context when a native library is unloaded
|
||||
// and getFromClass() will return null,
|
||||
static final NativeLibraryImpl UNLOADER =
|
||||
new NativeLibraryImpl(null, "dummy", false);
|
||||
new NativeLibraryImpl(null, "dummy", false, false);
|
||||
|
||||
final String name;
|
||||
final long handle;
|
||||
final boolean isBuiltin;
|
||||
final boolean isJNI;
|
||||
|
||||
Unloader(String name, long handle, boolean isBuiltin) {
|
||||
Unloader(String name, long handle, boolean isBuiltin, boolean isJNI) {
|
||||
assert !isBuiltin || isJNI : "a builtin native library must be JNI library";
|
||||
if (handle == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid handle for native library " + name);
|
||||
@ -323,18 +409,21 @@ public final class NativeLibraries {
|
||||
this.name = name;
|
||||
this.handle = handle;
|
||||
this.isBuiltin = isBuiltin;
|
||||
this.isJNI = isJNI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (NativeLibraries.loadedLibraryNames) {
|
||||
synchronized (loadedLibraryNames) {
|
||||
/* remove the native library name */
|
||||
NativeLibraries.loadedLibraryNames.remove(name);
|
||||
NativeLibraries.nativeLibraryContext.push(UNLOADER);
|
||||
if (!loadedLibraryNames.remove(name)) {
|
||||
throw new IllegalStateException(name + " has already been unloaded");
|
||||
}
|
||||
nativeLibraryContext.push(UNLOADER);
|
||||
try {
|
||||
unload(name, isBuiltin, handle);
|
||||
unload(name, isBuiltin, isJNI, handle);
|
||||
} finally {
|
||||
NativeLibraries.nativeLibraryContext.pop();
|
||||
nativeLibraryContext.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -371,8 +460,8 @@ public final class NativeLibraries {
|
||||
|
||||
// JNI FindClass expects the caller class if invoked from JNI_OnLoad
|
||||
// and JNI_OnUnload is NativeLibrary class
|
||||
private static native boolean load(NativeLibraryImpl impl, String name, boolean isBuiltin);
|
||||
private static native void unload(String name, boolean isBuiltin, long handle);
|
||||
private static native boolean load(NativeLibraryImpl impl, String name, boolean isBuiltin, boolean isJNI);
|
||||
private static native void unload(String name, boolean isBuiltin, boolean isJNI, long handle);
|
||||
private static native String findBuiltinLib(String name);
|
||||
private static native long findEntry0(NativeLibraryImpl lib, String name);
|
||||
}
|
||||
|
@ -31,15 +31,18 @@ package jdk.internal.loader;
|
||||
public interface NativeLibrary {
|
||||
String name();
|
||||
|
||||
/*
|
||||
/**
|
||||
* Finds the address of the entry of the given name. Returns 0
|
||||
* if not found.
|
||||
*
|
||||
* @param name the name of the symbol to be found
|
||||
*/
|
||||
long find(String name);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Finds the address of the entry of the given name.
|
||||
*
|
||||
* @param name the name of the symbol to be found
|
||||
* @throws NoSuchMethodException if the named entry is not found.
|
||||
*/
|
||||
default long lookup(String name) throws NoSuchMethodException {
|
||||
|
@ -109,11 +109,11 @@ static void *findJniFunction(JNIEnv *env, void *handle,
|
||||
/*
|
||||
* Class: jdk_internal_loader_NativeLibraries
|
||||
* Method: load
|
||||
* Signature: (Ljava/lang/String;Z)Z
|
||||
* Signature: (Ljava/lang/String;ZZ)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_jdk_internal_loader_NativeLibraries_load
|
||||
(JNIEnv *env, jobject this, jobject lib, jstring name, jboolean isBuiltin)
|
||||
(JNIEnv *env, jobject this, jobject lib, jstring name, jboolean isBuiltin, jboolean isJNI)
|
||||
{
|
||||
const char *cname;
|
||||
jint jniVersion;
|
||||
@ -128,50 +128,52 @@ Java_jdk_internal_loader_NativeLibraries_load
|
||||
if (cname == 0)
|
||||
return JNI_FALSE;
|
||||
handle = isBuiltin ? procHandle : JVM_LoadLibrary(cname);
|
||||
if (handle) {
|
||||
JNI_OnLoad_t JNI_OnLoad;
|
||||
JNI_OnLoad = (JNI_OnLoad_t)findJniFunction(env, handle,
|
||||
isBuiltin ? cname : NULL,
|
||||
JNI_TRUE);
|
||||
if (JNI_OnLoad) {
|
||||
JavaVM *jvm;
|
||||
(*env)->GetJavaVM(env, &jvm);
|
||||
jniVersion = (*JNI_OnLoad)(jvm, NULL);
|
||||
if (isJNI) {
|
||||
if (handle) {
|
||||
JNI_OnLoad_t JNI_OnLoad;
|
||||
JNI_OnLoad = (JNI_OnLoad_t)findJniFunction(env, handle,
|
||||
isBuiltin ? cname : NULL,
|
||||
JNI_TRUE);
|
||||
if (JNI_OnLoad) {
|
||||
JavaVM *jvm;
|
||||
(*env)->GetJavaVM(env, &jvm);
|
||||
jniVersion = (*JNI_OnLoad)(jvm, NULL);
|
||||
} else {
|
||||
jniVersion = 0x00010001;
|
||||
}
|
||||
|
||||
cause = (*env)->ExceptionOccurred(env);
|
||||
if (cause) {
|
||||
(*env)->ExceptionClear(env);
|
||||
(*env)->Throw(env, cause);
|
||||
if (!isBuiltin) {
|
||||
JVM_UnloadLibrary(handle);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!JVM_IsSupportedJNIVersion(jniVersion) ||
|
||||
(isBuiltin && jniVersion < JNI_VERSION_1_8)) {
|
||||
char msg[256];
|
||||
jio_snprintf(msg, sizeof(msg),
|
||||
"unsupported JNI version 0x%08X required by %s",
|
||||
jniVersion, cname);
|
||||
JNU_ThrowByName(env, "java/lang/UnsatisfiedLinkError", msg);
|
||||
if (!isBuiltin) {
|
||||
JVM_UnloadLibrary(handle);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
(*env)->SetIntField(env, lib, jniVersionID, jniVersion);
|
||||
} else {
|
||||
jniVersion = 0x00010001;
|
||||
}
|
||||
|
||||
cause = (*env)->ExceptionOccurred(env);
|
||||
if (cause) {
|
||||
(*env)->ExceptionClear(env);
|
||||
(*env)->Throw(env, cause);
|
||||
if (!isBuiltin) {
|
||||
JVM_UnloadLibrary(handle);
|
||||
cause = (*env)->ExceptionOccurred(env);
|
||||
if (cause) {
|
||||
(*env)->ExceptionClear(env);
|
||||
(*env)->SetLongField(env, lib, handleID, (jlong)0);
|
||||
(*env)->Throw(env, cause);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!JVM_IsSupportedJNIVersion(jniVersion) ||
|
||||
(isBuiltin && jniVersion < JNI_VERSION_1_8)) {
|
||||
char msg[256];
|
||||
jio_snprintf(msg, sizeof(msg),
|
||||
"unsupported JNI version 0x%08X required by %s",
|
||||
jniVersion, cname);
|
||||
JNU_ThrowByName(env, "java/lang/UnsatisfiedLinkError", msg);
|
||||
if (!isBuiltin) {
|
||||
JVM_UnloadLibrary(handle);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
(*env)->SetIntField(env, lib, jniVersionID, jniVersion);
|
||||
} else {
|
||||
cause = (*env)->ExceptionOccurred(env);
|
||||
if (cause) {
|
||||
(*env)->ExceptionClear(env);
|
||||
(*env)->SetLongField(env, lib, handleID, (jlong)0);
|
||||
(*env)->Throw(env, cause);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
(*env)->SetLongField(env, lib, handleID, ptr_to_jlong(handle));
|
||||
loaded = JNI_TRUE;
|
||||
@ -184,11 +186,11 @@ Java_jdk_internal_loader_NativeLibraries_load
|
||||
/*
|
||||
* Class: jdk_internal_loader_NativeLibraries
|
||||
* Method: unload
|
||||
* Signature: (Ljava/lang/String;ZJ)V
|
||||
* Signature: (Ljava/lang/String;ZZJ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_jdk_internal_loader_NativeLibraries_unload
|
||||
(JNIEnv *env, jclass cls, jstring name, jboolean isBuiltin, jlong address)
|
||||
(JNIEnv *env, jclass cls, jstring name, jboolean isBuiltin, jboolean isJNI, jlong address)
|
||||
{
|
||||
const char *onUnloadSymbols[] = JNI_ONUNLOAD_SYMBOLS;
|
||||
void *handle;
|
||||
@ -202,13 +204,15 @@ Java_jdk_internal_loader_NativeLibraries_unload
|
||||
return;
|
||||
}
|
||||
handle = jlong_to_ptr(address);
|
||||
JNI_OnUnload = (JNI_OnUnload_t )findJniFunction(env, handle,
|
||||
isBuiltin ? cname : NULL,
|
||||
JNI_FALSE);
|
||||
if (JNI_OnUnload) {
|
||||
JavaVM *jvm;
|
||||
(*env)->GetJavaVM(env, &jvm);
|
||||
(*JNI_OnUnload)(jvm, NULL);
|
||||
if (isJNI) {
|
||||
JNI_OnUnload = (JNI_OnUnload_t )findJniFunction(env, handle,
|
||||
isBuiltin ? cname : NULL,
|
||||
JNI_FALSE);
|
||||
if (JNI_OnUnload) {
|
||||
JavaVM *jvm;
|
||||
(*env)->GetJavaVM(env, &jvm);
|
||||
(*JNI_OnUnload)(jvm, NULL);
|
||||
}
|
||||
}
|
||||
if (!isBuiltin) {
|
||||
JVM_UnloadLibrary(handle);
|
||||
@ -299,5 +303,3 @@ Java_jdk_internal_loader_NativeLibraries_findBuiltinLib
|
||||
free(libName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
70
test/jdk/jdk/internal/loader/NativeLibraries/Main.java
Normal file
70
test/jdk/jdk/internal/loader/NativeLibraries/Main.java
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8240975
|
||||
* @modules java.base/jdk.internal.loader
|
||||
* @build java.base/* p.Test Main
|
||||
* @run main/othervm/native -Xcheck:jni Main
|
||||
* @summary Test loading and unloading of native libraries
|
||||
*/
|
||||
|
||||
import jdk.internal.loader.*;
|
||||
import jdk.internal.loader.NativeLibrariesTest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class Main {
|
||||
public static void main(String... args) throws Exception {
|
||||
setup();
|
||||
|
||||
NativeLibrariesTest test = new NativeLibrariesTest();
|
||||
test.runTest();
|
||||
|
||||
try {
|
||||
System.loadLibrary(NativeLibrariesTest.LIB_NAME);
|
||||
} catch (UnsatisfiedLinkError e) { e.printStackTrace(); }
|
||||
|
||||
// unload the native library and then System::loadLibrary should succeed
|
||||
test.unload();
|
||||
System.loadLibrary(NativeLibrariesTest.LIB_NAME);
|
||||
|
||||
// expect NativeLibraries to fail since the library has been loaded by System::loadLibrary
|
||||
try {
|
||||
test.load(false);
|
||||
} catch (UnsatisfiedLinkError e) { e.printStackTrace(); }
|
||||
}
|
||||
/*
|
||||
* move p/Test.class out from classpath to the scratch directory
|
||||
*/
|
||||
static void setup() throws IOException {
|
||||
String dir = System.getProperty("test.classes", ".");
|
||||
Path p = Files.createDirectories(Paths.get("classes").resolve("p"));
|
||||
Files.move(Paths.get(dir, "p", "Test.class"), p.resolve("Test.class"));
|
||||
}
|
||||
|
||||
}
|
135
test/jdk/jdk/internal/loader/NativeLibraries/java.base/jdk/internal/loader/NativeLibrariesTest.java
Normal file
135
test/jdk/jdk/internal/loader/NativeLibraries/java.base/jdk/internal/loader/NativeLibrariesTest.java
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.loader;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class NativeLibrariesTest implements Runnable {
|
||||
public static final String LIB_NAME = "nativeLibrariesTest";
|
||||
// increments when JNI_OnLoad and JNI_OnUnload is invoked.
|
||||
// This is only for JNI native library
|
||||
private static int loadedCount = 0;
|
||||
private static int unloadedCount = 0;
|
||||
/*
|
||||
* Called by JNI_OnLoad when the native library is unloaded
|
||||
*/
|
||||
static void nativeLibraryLoaded() {
|
||||
loadedCount++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by JNI_OnUnload when the native library is unloaded
|
||||
*/
|
||||
static void nativeLibraryUnloaded() {
|
||||
unloadedCount++;
|
||||
}
|
||||
|
||||
private final NativeLibraries nativeLibraries;
|
||||
public NativeLibrariesTest() {
|
||||
this.nativeLibraries = NativeLibraries.rawNativeLibraries(NativeLibraries.class, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke by p.Test to load the same native library from different class loader
|
||||
*/
|
||||
public void run() {
|
||||
load(true); // expect loading of native library succeed
|
||||
}
|
||||
|
||||
public void runTest() throws Exception {
|
||||
NativeLibrary nl1 = nativeLibraries.loadLibrary(LIB_NAME);
|
||||
NativeLibrary nl2 = nativeLibraries.loadLibrary(LIB_NAME);
|
||||
assertTrue(nl1 != null && nl2 != null, "fail to load library");
|
||||
assertTrue(nl1 == nl2, nl1 + " != " + nl2);
|
||||
assertTrue(loadedCount == 0, "Native library loaded. Expected: JNI_OnUnload not invoked");
|
||||
assertTrue(unloadedCount == 0, "native library never unloaded");
|
||||
|
||||
// load successfully even from another loader
|
||||
loadWithCustomLoader();
|
||||
|
||||
// unload the native library
|
||||
nativeLibraries.unload(nl1);
|
||||
assertTrue(unloadedCount == 0, "Native library unloaded. Expected: JNI_OnUnload not invoked");
|
||||
|
||||
// reload the native library and expect new NativeLibrary instance
|
||||
NativeLibrary nl3 = nativeLibraries.loadLibrary(LIB_NAME);
|
||||
assertTrue(nl1 != nl3, nl1 + " == " + nl3);
|
||||
assertTrue(loadedCount == 0, "Native library loaded. Expected: JNI_OnUnload not invoked");
|
||||
|
||||
// load successfully even from another loader
|
||||
loadWithCustomLoader();
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
NativeLibrary nl = nativeLibraries.loadLibrary(LIB_NAME);
|
||||
// unload the native library
|
||||
nativeLibraries.unload(nl);
|
||||
assertTrue(unloadedCount == 0, "Native library unloaded. Expected: JNI_OnUnload not invoked");
|
||||
}
|
||||
|
||||
public void load(boolean succeed) {
|
||||
NativeLibrary nl = nativeLibraries.loadLibrary(LIB_NAME);
|
||||
if (succeed) {
|
||||
assertTrue(nl != null, "fail to load library");
|
||||
} else {
|
||||
assertTrue(nl == null, "load library should fail");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads p.Test class with a new class loader and invokes the run() method.
|
||||
* p.Test::run invokes NativeLibrariesTest::run
|
||||
*/
|
||||
private void loadWithCustomLoader() throws Exception {
|
||||
TestLoader loader = new TestLoader();
|
||||
Class<?> c = Class.forName("p.Test", true, loader);
|
||||
Constructor<?> ctr = c.getConstructor(Runnable.class);
|
||||
Runnable r = (Runnable) ctr.newInstance(this);
|
||||
r.run();
|
||||
}
|
||||
|
||||
static class TestLoader extends URLClassLoader {
|
||||
static URL[] toURLs() {
|
||||
try {
|
||||
return new URL[] { Paths.get("classes").toUri().toURL() };
|
||||
} catch (MalformedURLException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
TestLoader() {
|
||||
super("testloader", toURLs(), ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
}
|
||||
|
||||
static void assertTrue(boolean value, String msg) {
|
||||
if (!value) {
|
||||
throw new AssertionError(msg);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "jni.h"
|
||||
#include "jni_util.h"
|
||||
|
||||
static jclass test_class;
|
||||
static jint current_jni_version = JNI_VERSION_10;
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
JNIEnv *env;
|
||||
jclass cl;
|
||||
jmethodID mid;
|
||||
|
||||
(*vm)->GetEnv(vm, (void **) &env, current_jni_version);
|
||||
|
||||
cl = (*env)->FindClass(env, "jdk/internal/loader/NativeLibrariesTest");
|
||||
test_class = (*env)->NewGlobalRef(env, cl);
|
||||
|
||||
mid = (*env)->GetStaticMethodID(env, test_class, "nativeLibraryLoaded", "()V");
|
||||
(*env)->CallStaticVoidMethod(env, test_class, mid);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
(*env)->ExceptionDescribe(env);
|
||||
(*env)->FatalError(env, "Exception thrown");
|
||||
}
|
||||
return current_jni_version;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
JNI_OnUnload(JavaVM *vm, void *reserved) {
|
||||
JNIEnv *env;
|
||||
jmethodID mid;
|
||||
|
||||
(*vm)->GetEnv(vm, (void **) &env, current_jni_version);
|
||||
mid = (*env)->GetStaticMethodID(env, test_class, "nativeLibraryUnloaded", "()V");
|
||||
(*env)->CallStaticVoidMethod(env, test_class, mid);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
(*env)->ExceptionDescribe(env);
|
||||
(*env)->FatalError(env, "Exception thrown");
|
||||
}
|
||||
}
|
37
test/jdk/jdk/internal/loader/NativeLibraries/p/Test.java
Normal file
37
test/jdk/jdk/internal/loader/NativeLibraries/p/Test.java
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package p;
|
||||
|
||||
public class Test implements Runnable {
|
||||
private final Runnable r;
|
||||
public Test(Runnable r) {
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the native library is loaded.
|
||||
*/
|
||||
public void run() {
|
||||
r.run();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user