Add comment on SnappyLoader.load
This commit is contained in:
parent
7931742179
commit
199b12e61c
|
@ -36,7 +36,6 @@ import java.net.URL;
|
||||||
import java.security.DigestInputStream;
|
import java.security.DigestInputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.ProtectionDomain;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
@ -91,6 +90,7 @@ public class SnappyLoader
|
||||||
cl = cl.getParent();
|
cl = cl.getParent();
|
||||||
}
|
}
|
||||||
return cl;
|
return cl;
|
||||||
|
//return ClassLoader.getSystemClassLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] getByteCode(String resourcePath) throws IOException {
|
private static byte[] getByteCode(String resourcePath) throws IOException {
|
||||||
|
@ -112,6 +112,28 @@ public class SnappyLoader
|
||||||
return isLoaded;
|
return isLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load SnappyNative and its JNI native implementation in the root class
|
||||||
|
* loader. This is necessary to avoid JNI multi-loading issues when the same
|
||||||
|
* JNI library is loaded by different class loaders in the same JVM.
|
||||||
|
*
|
||||||
|
* In order to load native code in the root class loader, this method first
|
||||||
|
* inject SnappyNativeLoader class into the root class loader, because
|
||||||
|
* {@link System#load(String)} method uses the class loader of the caller
|
||||||
|
* class when loading native libraries.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* (root class loader) -> [SnappyNativeLoader, SnappyNative, SnappyNativeAPI, SnappyErrorCode] (injected by this method)
|
||||||
|
* |
|
||||||
|
* |
|
||||||
|
* (child class loader) -> See the above classes loaded by the root class loader.
|
||||||
|
* Then creates SnappyNativeAPI implementation by instantiating SnappyNaitive class.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
static synchronized SnappyNativeAPI load() {
|
static synchronized SnappyNativeAPI load() {
|
||||||
|
|
||||||
if (isInitialized)
|
if (isInitialized)
|
||||||
|
@ -119,9 +141,9 @@ public class SnappyLoader
|
||||||
|
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
final String nativeLoaderClassName = "org.xerial.snappy.SnappyNativeLoader";
|
final String nativeLoaderClassName = "org.xerial.snappy.SnappyNativeLoader";
|
||||||
final String[] classesToPreload = new String[] { "org.xerial.snappy.SnappyNativeAPI",
|
|
||||||
"org.xerial.snappy.SnappyNative", "org.xerial.snappy.SnappyErrorCode" };
|
|
||||||
|
|
||||||
|
// Use parent class loader to load SnappyNative, since Tomcat, which uses different class loaders for each webapps, cannot load JNI interface twice
|
||||||
|
ClassLoader rootClassLoader = getRootClassLoader();
|
||||||
try {
|
try {
|
||||||
Class< ? > c = Class.forName(nativeLoaderClassName);
|
Class< ? > c = Class.forName(nativeLoaderClassName);
|
||||||
// If this native loader class is already defined, it means that another class loader already loaded the native library of snappy
|
// If this native loader class is already defined, it means that another class loader already loaded the native library of snappy
|
||||||
|
@ -131,9 +153,12 @@ public class SnappyLoader
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException e) {
|
catch (ClassNotFoundException e) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// Load a byte code
|
// Load a byte code
|
||||||
byte[] byteCode = getByteCode("/org/xerial/snappy/SnappyNativeLoader.bytecode");
|
byte[] byteCode = getByteCode("/org/xerial/snappy/SnappyNativeLoader.bytecode");
|
||||||
// In addition, we need to load the other dependent classes (e.g., SnappyNative and SnappyException) using the system class loader
|
// In addition, we need to load the other dependent classes (e.g., SnappyNative and SnappyException) using the system class loader
|
||||||
|
final String[] classesToPreload = new String[] { "org.xerial.snappy.SnappyNativeAPI",
|
||||||
|
"org.xerial.snappy.SnappyNative", "org.xerial.snappy.SnappyErrorCode" };
|
||||||
List<byte[]> preloadClassByteCode = new ArrayList<byte[]>(classesToPreload.length);
|
List<byte[]> preloadClassByteCode = new ArrayList<byte[]>(classesToPreload.length);
|
||||||
for (String each : classesToPreload) {
|
for (String each : classesToPreload) {
|
||||||
preloadClassByteCode.add(getByteCode(String.format("/%s.class", each.replaceAll("\\.", "/"))));
|
preloadClassByteCode.add(getByteCode(String.format("/%s.class", each.replaceAll("\\.", "/"))));
|
||||||
|
@ -142,21 +167,19 @@ public class SnappyLoader
|
||||||
// Create SnappyNative class from a byte code
|
// Create SnappyNative class from a byte code
|
||||||
Class< ? > classLoader = Class.forName("java.lang.ClassLoader");
|
Class< ? > classLoader = Class.forName("java.lang.ClassLoader");
|
||||||
Method defineClass = classLoader.getDeclaredMethod("defineClass", new Class[] { String.class,
|
Method defineClass = classLoader.getDeclaredMethod("defineClass", new Class[] { String.class,
|
||||||
byte[].class, int.class, int.class, ProtectionDomain.class });
|
byte[].class, int.class, int.class });
|
||||||
|
|
||||||
// Use parent class loader to load SnappyNative, since Tomcat, which uses different class loaders for each webapps, cannot load JNI interface twice
|
//ProtectionDomain pd = System.class.getProtectionDomain();
|
||||||
ClassLoader systemClassLoader = getRootClassLoader();
|
|
||||||
ProtectionDomain pd = System.class.getProtectionDomain();
|
|
||||||
|
|
||||||
// ClassLoader.defineClass is a protected method, so we have to make it accessible
|
// ClassLoader.defineClass is a protected method, so we have to make it accessible
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
try {
|
try {
|
||||||
// Create a new class using a ClassLoader#defineClass
|
// Create a new class using a ClassLoader#defineClass
|
||||||
defineClass.invoke(systemClassLoader, nativeLoaderClassName, byteCode, 0, byteCode.length, pd);
|
defineClass.invoke(rootClassLoader, nativeLoaderClassName, byteCode, 0, byteCode.length);
|
||||||
|
|
||||||
for (int i = 0; i < classesToPreload.length; ++i) {
|
for (int i = 0; i < classesToPreload.length; ++i) {
|
||||||
byte[] b = preloadClassByteCode.get(i);
|
byte[] b = preloadClassByteCode.get(i);
|
||||||
defineClass.invoke(systemClassLoader, classesToPreload[i], b, 0, b.length, pd);
|
defineClass.invoke(rootClassLoader, classesToPreload[i], b, 0, b.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
@ -164,14 +187,10 @@ public class SnappyLoader
|
||||||
defineClass.setAccessible(false);
|
defineClass.setAccessible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the snappy loader class
|
// Load the SnappyNativeLoader class
|
||||||
Class< ? > loaderClass = systemClassLoader.loadClass(nativeLoaderClassName);
|
Class< ? > loaderClass = rootClassLoader.loadClass(nativeLoaderClassName);
|
||||||
if (loaderClass != null) {
|
if (loaderClass != null) {
|
||||||
|
|
||||||
final String KEY_SNAPPY_NATIVE_LOAD_FLAG = "org.xerial.snappy.native_is_loaded";
|
|
||||||
boolean isNativeLibLoaded = Boolean.parseBoolean(System.getProperty(KEY_SNAPPY_NATIVE_LOAD_FLAG,
|
|
||||||
"false"));
|
|
||||||
if (!isNativeLibLoaded) {
|
|
||||||
File nativeLib = findNativeLibrary();
|
File nativeLib = findNativeLibrary();
|
||||||
if (nativeLib != null) {
|
if (nativeLib != null) {
|
||||||
// Load extracted or specified snappyjava native library.
|
// Load extracted or specified snappyjava native library.
|
||||||
|
@ -180,20 +199,15 @@ public class SnappyLoader
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Load preinstalled snappyjava (in the path -Djava.library.path)
|
// Load preinstalled snappyjava (in the path -Djava.library.path)
|
||||||
Method loadMethod = loaderClass.getDeclaredMethod("loadLibrary",
|
Method loadMethod = loaderClass.getDeclaredMethod("loadLibrary", new Class[] { String.class });
|
||||||
new Class[] { String.class });
|
|
||||||
loadMethod.invoke(null, "snappyjava");
|
loadMethod.invoke(null, "snappyjava");
|
||||||
}
|
}
|
||||||
|
|
||||||
System.setProperty(KEY_SNAPPY_NATIVE_LOAD_FLAG, "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
// And also, preload the other dependent classes
|
// And also, preload the other dependent classes
|
||||||
for (String each : classesToPreload) {
|
for (String each : classesToPreload) {
|
||||||
systemClassLoader.loadClass(each);
|
rootClassLoader.loadClass(each);
|
||||||
}
|
}
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
|
|
||||||
api = (SnappyNativeAPI) Class.forName("org.xerial.snappy.SnappyNative").newInstance();
|
api = (SnappyNativeAPI) Class.forName("org.xerial.snappy.SnappyNative").newInstance();
|
||||||
return api;
|
return api;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ import java.nio.ByteBuffer;
|
||||||
* method in this class is defined in SnappyNative.h (genereted by javah) and
|
* method in this class is defined in SnappyNative.h (genereted by javah) and
|
||||||
* SnappyNative.cpp
|
* SnappyNative.cpp
|
||||||
*
|
*
|
||||||
|
* NEVER USE THIS CLASS DIRECTROY since it breaks the native library code
|
||||||
|
* loading.
|
||||||
*
|
*
|
||||||
* @author leo
|
* @author leo
|
||||||
*
|
*
|
||||||
|
|
|
@ -96,6 +96,7 @@ public class SnappyLoaderTest
|
||||||
ClassRealm L2 = cw.newRealm("l2", URLClassLoader.newInstance(new URL[] { classPath }, parent));
|
ClassRealm L2 = cw.newRealm("l2", URLClassLoader.newInstance(new URL[] { classPath }, parent));
|
||||||
|
|
||||||
// Actually load Snappy.class in a class loader
|
// Actually load Snappy.class in a class loader
|
||||||
|
try {
|
||||||
Class< ? > S1 = L1.loadClass("org.xerial.snappy.Snappy");
|
Class< ? > S1 = L1.loadClass("org.xerial.snappy.Snappy");
|
||||||
Method m = S1.getMethod("getNativeLibraryVersion");
|
Method m = S1.getMethod("getNativeLibraryVersion");
|
||||||
String v = (String) m.invoke(null);
|
String v = (String) m.invoke(null);
|
||||||
|
@ -106,6 +107,11 @@ public class SnappyLoaderTest
|
||||||
String v2 = (String) m2.invoke(null);
|
String v2 = (String) m2.invoke(null);
|
||||||
|
|
||||||
assertEquals(v, v2);
|
assertEquals(v, v2);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue