diff --git a/src/main/java/org/xerial/snappy/SnappyLoader.java b/src/main/java/org/xerial/snappy/SnappyLoader.java
index 335a4dd..67ccfe8 100755
--- a/src/main/java/org/xerial/snappy/SnappyLoader.java
+++ b/src/main/java/org/xerial/snappy/SnappyLoader.java
@@ -36,7 +36,6 @@ import java.net.URL;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
@@ -91,6 +90,7 @@ public class SnappyLoader
cl = cl.getParent();
}
return cl;
+ //return ClassLoader.getSystemClassLoader();
}
private static byte[] getByteCode(String resourcePath) throws IOException {
@@ -112,6 +112,28 @@ public class SnappyLoader
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.
+ *
+ *
+ * (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.
+ *
+ *
+ *
+ *
+ * @return
+ */
static synchronized SnappyNativeAPI load() {
if (isInitialized)
@@ -119,9 +141,9 @@ public class SnappyLoader
isInitialized = true;
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 {
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
@@ -131,9 +153,12 @@ public class SnappyLoader
}
catch (ClassNotFoundException e) {
try {
+
// Load a byte code
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
+ final String[] classesToPreload = new String[] { "org.xerial.snappy.SnappyNativeAPI",
+ "org.xerial.snappy.SnappyNative", "org.xerial.snappy.SnappyErrorCode" };
List preloadClassByteCode = new ArrayList(classesToPreload.length);
for (String each : classesToPreload) {
preloadClassByteCode.add(getByteCode(String.format("/%s.class", each.replaceAll("\\.", "/"))));
@@ -142,21 +167,19 @@ public class SnappyLoader
// Create SnappyNative class from a byte code
Class< ? > classLoader = Class.forName("java.lang.ClassLoader");
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
- ClassLoader systemClassLoader = getRootClassLoader();
- ProtectionDomain pd = System.class.getProtectionDomain();
+ //ProtectionDomain pd = System.class.getProtectionDomain();
// ClassLoader.defineClass is a protected method, so we have to make it accessible
defineClass.setAccessible(true);
try {
// 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) {
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 {
@@ -164,36 +187,27 @@ public class SnappyLoader
defineClass.setAccessible(false);
}
- // Load the snappy loader class
- Class< ? > loaderClass = systemClassLoader.loadClass(nativeLoaderClassName);
+ // Load the SnappyNativeLoader class
+ Class< ? > loaderClass = rootClassLoader.loadClass(nativeLoaderClassName);
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();
- if (nativeLib != null) {
- // Load extracted or specified snappyjava native library.
- Method loadMethod = loaderClass.getDeclaredMethod("load", new Class[] { String.class });
- loadMethod.invoke(null, nativeLib.getAbsolutePath());
- }
- else {
- // Load preinstalled snappyjava (in the path -Djava.library.path)
- Method loadMethod = loaderClass.getDeclaredMethod("loadLibrary",
- new Class[] { String.class });
- loadMethod.invoke(null, "snappyjava");
- }
-
- System.setProperty(KEY_SNAPPY_NATIVE_LOAD_FLAG, "true");
+ File nativeLib = findNativeLibrary();
+ if (nativeLib != null) {
+ // Load extracted or specified snappyjava native library.
+ Method loadMethod = loaderClass.getDeclaredMethod("load", new Class[] { String.class });
+ loadMethod.invoke(null, nativeLib.getAbsolutePath());
+ }
+ else {
+ // Load preinstalled snappyjava (in the path -Djava.library.path)
+ Method loadMethod = loaderClass.getDeclaredMethod("loadLibrary", new Class[] { String.class });
+ loadMethod.invoke(null, "snappyjava");
}
// And also, preload the other dependent classes
for (String each : classesToPreload) {
- systemClassLoader.loadClass(each);
+ rootClassLoader.loadClass(each);
}
isLoaded = true;
-
api = (SnappyNativeAPI) Class.forName("org.xerial.snappy.SnappyNative").newInstance();
return api;
}
diff --git a/src/main/java/org/xerial/snappy/SnappyNative.java b/src/main/java/org/xerial/snappy/SnappyNative.java
index eea41f4..978e5e5 100755
--- a/src/main/java/org/xerial/snappy/SnappyNative.java
+++ b/src/main/java/org/xerial/snappy/SnappyNative.java
@@ -32,6 +32,8 @@ import java.nio.ByteBuffer;
* method in this class is defined in SnappyNative.h (genereted by javah) and
* SnappyNative.cpp
*
+ * NEVER USE THIS CLASS DIRECTROY since it breaks the native library code
+ * loading.
*
* @author leo
*
diff --git a/src/test/java/org/xerial/snappy/SnappyLoaderTest.java b/src/test/java/org/xerial/snappy/SnappyLoaderTest.java
index db862f7..d93d8ff 100755
--- a/src/test/java/org/xerial/snappy/SnappyLoaderTest.java
+++ b/src/test/java/org/xerial/snappy/SnappyLoaderTest.java
@@ -96,16 +96,22 @@ public class SnappyLoaderTest
ClassRealm L2 = cw.newRealm("l2", URLClassLoader.newInstance(new URL[] { classPath }, parent));
// Actually load Snappy.class in a class loader
- Class< ? > S1 = L1.loadClass("org.xerial.snappy.Snappy");
- Method m = S1.getMethod("getNativeLibraryVersion");
- String v = (String) m.invoke(null);
+ try {
+ Class< ? > S1 = L1.loadClass("org.xerial.snappy.Snappy");
+ Method m = S1.getMethod("getNativeLibraryVersion");
+ String v = (String) m.invoke(null);
- // Load Snappy.class from another class loader
- Class< ? > S2 = L2.loadClass("org.xerial.snappy.Snappy");
- Method m2 = S2.getMethod("getNativeLibraryVersion");
- String v2 = (String) m2.invoke(null);
+ // Load Snappy.class from another class loader
+ Class< ? > S2 = L2.loadClass("org.xerial.snappy.Snappy");
+ Method m2 = S2.getMethod("getNativeLibraryVersion");
+ String v2 = (String) m2.invoke(null);
- assertEquals(v, v2);
+ assertEquals(v, v2);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
}