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:
parent
e5d4e28e46
commit
b12a1e3fa6
@ -136,13 +136,22 @@ public final class NativeLibraryLoader {
|
|||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
File tmpFile = null;
|
File tmpFile = null;
|
||||||
URL url = loader.getResource(path);
|
URL url;
|
||||||
|
if (loader == null) {
|
||||||
|
url = ClassLoader.getSystemResource(path);
|
||||||
|
} else {
|
||||||
|
url = loader.getResource(path);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
if (PlatformDependent.isOsx()) {
|
if (PlatformDependent.isOsx()) {
|
||||||
String fileName = path.endsWith(".jnilib") ? NATIVE_RESOURCE_HOME + "lib" + name + ".dynlib" :
|
String fileName = path.endsWith(".jnilib") ? NATIVE_RESOURCE_HOME + "lib" + name + ".dynlib" :
|
||||||
NATIVE_RESOURCE_HOME + "lib" + name + ".jnilib";
|
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) {
|
if (url == null) {
|
||||||
FileNotFoundException fnf = new FileNotFoundException(fileName);
|
FileNotFoundException fnf = new FileNotFoundException(fileName);
|
||||||
ThrowableUtil.addSuppressedAndClear(fnf, suppressed);
|
ThrowableUtil.addSuppressedAndClear(fnf, suppressed);
|
||||||
@ -280,8 +289,12 @@ public final class NativeLibraryLoader {
|
|||||||
private static Class<?> tryToLoadClass(final ClassLoader loader, final Class<?> helper)
|
private static Class<?> tryToLoadClass(final ClassLoader loader, final Class<?> helper)
|
||||||
throws ClassNotFoundException {
|
throws ClassNotFoundException {
|
||||||
try {
|
try {
|
||||||
return loader.loadClass(helper.getName());
|
return Class.forName(helper.getName(), false, loader);
|
||||||
} catch (ClassNotFoundException e1) {
|
} catch (ClassNotFoundException e1) {
|
||||||
|
if (loader == null) {
|
||||||
|
// cannot defineClass inside bootstrap class loader
|
||||||
|
throw e1;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// The helper class is NOT found in target ClassLoader, we have to define the helper class.
|
// The helper class is NOT found in target ClassLoader, we have to define the helper class.
|
||||||
final byte[] classBinary = classToByteArray(helper);
|
final byte[] classBinary = classToByteArray(helper);
|
||||||
|
@ -17,6 +17,7 @@ package io.netty.util.internal;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -25,6 +26,8 @@ import static org.junit.Assert.fail;
|
|||||||
|
|
||||||
public class NativeLibraryLoaderTest {
|
public class NativeLibraryLoaderTest {
|
||||||
|
|
||||||
|
private static final Method getSupressedMethod = getGetSuppressed();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFileNotFound() {
|
public void testFileNotFound() {
|
||||||
try {
|
try {
|
||||||
@ -32,6 +35,47 @@ public class NativeLibraryLoaderTest {
|
|||||||
fail();
|
fail();
|
||||||
} catch (UnsatisfiedLinkError error) {
|
} catch (UnsatisfiedLinkError error) {
|
||||||
assertTrue(error.getCause() instanceof FileNotFoundException);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user