diff --git a/src/main/java/org/xerial/snappy/SnappyLoader.java b/src/main/java/org/xerial/snappy/SnappyLoader.java
index 6fa066a..5f8f7f4 100755
--- a/src/main/java/org/xerial/snappy/SnappyLoader.java
+++ b/src/main/java/org/xerial/snappy/SnappyLoader.java
@@ -25,17 +25,13 @@
package org.xerial.snappy;
import java.io.*;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
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.Enumeration;
-import java.util.List;
import java.util.Properties;
+import java.util.UUID;
/**
* Internal only - Do not use this class. This class loads a native
@@ -136,92 +132,13 @@ public class SnappyLoader
loadSnappySystemProperties();
}
- private static ClassLoader getRootClassLoader() {
- ClassLoader cl = Thread.currentThread().getContextClassLoader();
- while (cl.getParent() != null) {
- cl = cl.getParent();
- }
- return cl;
- }
-
- private static byte[] getByteCode(String resourcePath) throws IOException {
-
- InputStream in = SnappyLoader.class.getResourceAsStream(resourcePath);
- if (in == null)
- throw new IOException(resourcePath + " is not found");
- byte[] buf = new byte[1024];
- ByteArrayOutputStream byteCodeBuf = new ByteArrayOutputStream();
- for (int readLength; (readLength = in.read(buf)) != -1;) {
- byteCodeBuf.write(buf, 0, readLength);
- }
- in.close();
-
- return byteCodeBuf.toByteArray();
- }
-
- public static boolean isNativeLibraryLoaded() {
- return isLoaded;
- }
-
- private static boolean hasInjectedNativeLoader() {
- try {
- final String nativeLoaderClassName = "org.xerial.snappy.SnappyNativeLoader";
- 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
- return true;
- }
- catch (ClassNotFoundException e) {
- // do loading
- return false;
- }
- }
-
- /**
- * Load SnappyNative and its JNI native implementation using the root class
- * loader. This hack is for avoiding the JNI multi-loading issue when the
- * same JNI library is loaded by different class loaders.
- *
- * 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 (load JNI code), SnappyNative (has native methods), SnappyNativeAPI, SnappyErrorCode] (injected by this method)
- * |
- * |
- * (child class loader) -> Sees the above classes loaded by the root class loader.
- * Then creates SnappyNativeAPI implementation by instantiating SnappyNaitive class.
- *
- *
- *
- *
- * (root class loader) -> [SnappyNativeLoader, SnappyNative ...] -> native code is loaded by once in this class loader
- * | \
- * | (child2 class loader)
- * (child1 class loader)
- *
- * child1 and child2 share the same SnappyNative code loaded by the root class loader.
- *
- *
- * Note that Java's class loader first delegates the class lookup to its
- * parent class loader. So once SnappyNativeLoader is loaded by the root
- * class loader, no child class loader initialize SnappyNativeLoader again.
- *
- * @return
- */
- static synchronized Object load()
+ static synchronized Object load()
{
if (api != null)
return api;
try {
- if (!hasInjectedNativeLoader()) {
- // Inject SnappyNativeLoader (src/main/resources/org/xerial/snappy/SnappyNativeLoader.bytecode) to the root class loader
- Class< ? > nativeLoader = injectSnappyNativeLoader();
- // Load the JNI code using the injected loader
- loadNativeLibrary(nativeLoader);
- }
+ loadNativeLibrary();
isLoaded = true;
// Look up SnappyNative, injected to the root classloder, using reflection in order to avoid the initialization of SnappyNative class in this context class loader.
@@ -237,91 +154,23 @@ public class SnappyLoader
}
/**
- * Inject SnappyNativeLoader class to the root class loader
- *
- * @return native code loader class initialized in the root class loader
+ * Load a native library of snappy-java
*/
- private static Class< ? > injectSnappyNativeLoader() {
-
- try {
- // Use parent class loader to load SnappyNative, since Tomcat, which uses different class loaders for each webapps, cannot load JNI interface twice
-
- final String nativeLoaderClassName = "org.xerial.snappy.SnappyNativeLoader";
- ClassLoader rootClassLoader = getRootClassLoader();
- // 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("\\.", "/"))));
- }
-
- // Create SnappyNativeLoader 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 });
-
- 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(rootClassLoader, nativeLoaderClassName, byteCode, 0, byteCode.length, pd);
-
- // And also define dependent classes in the root class loader
- for (int i = 0; i < classesToPreload.length; ++i) {
- byte[] b = preloadClassByteCode.get(i);
- defineClass.invoke(rootClassLoader, classesToPreload[i], b, 0, b.length, pd);
- }
- }
- finally {
- // Reset the accessibility to defineClass method
- defineClass.setAccessible(false);
- }
-
- // Load the SnappyNativeLoader class
- return rootClassLoader.loadClass(nativeLoaderClassName);
-
- }
- catch (Exception e) {
- e.printStackTrace(System.err);
- throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, e.getMessage());
- }
-
- }
-
- /**
- * Load snappy-java's native code using load method of the
- * SnappyNativeLoader class injected to the root class loader.
- *
- * @param loaderClass
- * @throws SecurityException
- * @throws NoSuchMethodException
- * @throws IllegalArgumentException
- * @throws IllegalAccessException
- * @throws InvocationTargetException
- */
- private static void loadNativeLibrary(Class< ? > loaderClass) throws SecurityException, NoSuchMethodException,
- IllegalArgumentException, IllegalAccessException, InvocationTargetException {
- if (loaderClass == null)
- throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, "missing snappy native loader class");
+ private static void loadNativeLibrary() {
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());
+ // Load extracted or specified snappyjava native library.
+ System.load(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");
+ // Load preinstalled snappyjava (in the path -Djava.library.path)
+ System.loadLibrary("snappyjava");
}
}
+
+
/**
* Computes the MD5 value of the input stream
*
@@ -379,35 +228,14 @@ public class SnappyLoader
*/
private static File extractLibraryFile(String libFolderForCurrentOS, String libraryFileName, String targetFolder) {
String nativeLibraryFilePath = libFolderForCurrentOS + "/" + libraryFileName;
- final String prefix = "snappy-" + getVersion() + "-";
- String extractedLibFileName = prefix + libraryFileName;
- File extractedLibFile = new File(targetFolder, extractedLibFileName);
+ // Attach UUID to the native library file to ensure multiple class loaders can read the libsnappy-java multiple times.
+ String uuid = UUID.randomUUID().toString();
+ String extractedLibFileName = String.format("snappy-%s-%s-%s", getVersion(), uuid, libraryFileName);
+ File extractedLibFile = new File(targetFolder, extractedLibFileName);
+ // Delete extracted lib file on exit.
+ extractedLibFile.deleteOnExit();
try {
- if (extractedLibFile.exists()) {
- // Compare the native library contents
- InputStream nativeIn = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
- InputStream extractedLibIn = new FileInputStream(extractedLibFile);
- try {
- if(contentsEquals(nativeIn, extractedLibIn)) {
- return new File(targetFolder, extractedLibFileName);
- }
- else {
- // remove old native library file
- boolean deletionSucceeded = extractedLibFile.delete();
- if (!deletionSucceeded) {
- throw new IOException("failed to remove existing native library file: "
- + extractedLibFile.getAbsolutePath());
- }
- }
- }
- finally {
- if(nativeIn != null)
- nativeIn.close();
- if(extractedLibIn != null)
- extractedLibIn.close();
- }
- }
// Extract a native library file into the target directory
InputStream reader = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
@@ -435,6 +263,22 @@ public class SnappyLoader
catch (Throwable e) {}
}
+ // Check the contents
+ {
+ InputStream nativeIn = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
+ InputStream extractedLibIn = new FileInputStream(extractedLibFile);
+ try {
+ if(!contentsEquals(nativeIn, extractedLibIn))
+ throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, String.format("Failed to write a native library file at %s", extractedLibFile));
+ }
+ finally {
+ if(nativeIn != null)
+ nativeIn.close();
+ if(extractedLibIn != null)
+ extractedLibIn.close();
+ }
+ }
+
return new File(targetFolder, extractedLibFileName);
}
catch (IOException e) {
@@ -485,7 +329,7 @@ public class SnappyLoader
throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, errorMessage);
}
- // Temporary library folder. Use the value of org.xerial.snappy.tempdir or java.io.tmpdir
+ // Temporary folder for the native lib. Use the value of org.xerial.snappy.tempdir or java.io.tmpdir
String tempFolder = new File(System.getProperty(KEY_SNAPPY_TEMPDIR,
System.getProperty("java.io.tmpdir"))).getAbsolutePath();
@@ -500,8 +344,6 @@ public class SnappyLoader
-
-
/**
* Get the snappy-java version by reading pom.properties embedded in jar.
* This version data is used as a suffix of a dll file extracted from the