Support running Netty in bootstrap class loader

Motivation:

Fix NullPointerExceptions that occur when running netty-tcnative inside the bootstrap class loader.

Modifications:

- Replace loader.getResource(...) with ClassLoader.getSystemResource(...) when loader is null.
- Replace loader.loadClass(...) with Class.forName(..., false, loader) which works when loader is both null and non-null.

Result:

Support running native libs in bootstrap class loader
This commit is contained in:
Trask Stalnaker 2017-10-25 20:05:42 -07:00 committed by Norman Maurer
parent 911b2acc50
commit 58e74e9fee
2 changed files with 60 additions and 3 deletions

View File

@ -136,13 +136,22 @@ public final class NativeLibraryLoader {
InputStream in = null;
OutputStream out = null;
File tmpFile = null;
URL url = loader.getResource(path);
URL url;
if (loader == null) {
url = ClassLoader.getSystemResource(path);
} else {
url = loader.getResource(path);
}
try {
if (url == null) {
if (PlatformDependent.isOsx()) {
String fileName = path.endsWith(".jnilib") ? NATIVE_RESOURCE_HOME + "lib" + name + ".dynlib" :
NATIVE_RESOURCE_HOME + "lib" + name + ".jnilib";
url = loader.getResource(fileName);
if (loader == null) {
url = ClassLoader.getSystemResource(fileName);
} else {
url = loader.getResource(fileName);
}
if (url == null) {
FileNotFoundException fnf = new FileNotFoundException(fileName);
ThrowableUtil.addSuppressedAndClear(fnf, suppressed);
@ -280,8 +289,12 @@ public final class NativeLibraryLoader {
private static Class<?> tryToLoadClass(final ClassLoader loader, final Class<?> helper)
throws ClassNotFoundException {
try {
return loader.loadClass(helper.getName());
return Class.forName(helper.getName(), false, loader);
} catch (ClassNotFoundException e1) {
if (loader == null) {
// cannot defineClass inside bootstrap class loader
throw e1;
}
try {
// The helper class is NOT found in target ClassLoader, we have to define the helper class.
final byte[] classBinary = classToByteArray(helper);

View File

@ -17,6 +17,7 @@ package io.netty.util.internal;
import org.junit.Test;
import java.lang.reflect.Method;
import java.io.FileNotFoundException;
import java.util.UUID;
@ -25,6 +26,8 @@ import static org.junit.Assert.fail;
public class NativeLibraryLoaderTest {
private static final Method getSupressedMethod = getGetSuppressed();
@Test
public void testFileNotFound() {
try {
@ -32,6 +35,47 @@ public class NativeLibraryLoaderTest {
fail();
} catch (UnsatisfiedLinkError error) {
assertTrue(error.getCause() instanceof FileNotFoundException);
if (getSupressedMethod != null) {
verifySuppressedException(error, UnsatisfiedLinkError.class);
}
}
}
@Test
public void testFileNotFoundWithNullClassLoader() {
try {
NativeLibraryLoader.load(UUID.randomUUID().toString(), null);
fail();
} catch (UnsatisfiedLinkError error) {
assertTrue(error.getCause() instanceof FileNotFoundException);
if (getSupressedMethod != null) {
verifySuppressedException(error, ClassNotFoundException.class);
}
}
}
private static void verifySuppressedException(UnsatisfiedLinkError error,
Class<?> expectedSuppressedExceptionClass) {
try {
Throwable[] suppressed = (Throwable[]) getSupressedMethod.invoke(error.getCause());
assertTrue(suppressed.length == 1);
assertTrue(suppressed[0] instanceof UnsatisfiedLinkError);
suppressed = (Throwable[]) getSupressedMethod.invoke(suppressed[0]);
assertTrue(suppressed.length == 1);
assertTrue(expectedSuppressedExceptionClass.isInstance(suppressed[0]));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Method getGetSuppressed() {
if (PlatformDependent.javaVersion() < 7) {
return null;
}
try {
return Throwable.class.getDeclaredMethod("getSuppressed");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}