diff --git a/pom.xml b/pom.xml index 11651b0..93d294d 100755 --- a/pom.xml +++ b/pom.xml @@ -182,12 +182,22 @@ org.osgi.framework;version="[1.5,2)" lazy +<<<<<<< HEAD org/xerial/snappy/native/Windows/amd64/snappyjava.dll;osname=win32;processor=x86-64, org/xerial/snappy/native/Windows/x86/snappyjava.dll;osname=win32;processor=x86, org/xerial/snappy/native/Mac/x86_64/libsnappyjava.jnilib;osname=macosx;processor=x86-64, org/xerial/snappy/native/Linux/amd64/libsnappyjava.so;osname=linux;processor=x86-64, org/xerial/snappy/native/Linux/i386/libsnappyjava.so;osname=linux;processor=x86, org/xerial/snappy/native/Linux/arm/libsnappyjava.so;osname=linux;processor=arm +======= + org/xerial/snappy/native/Windows/x86_64/snappyjava.dll;selection-filter="(&(osgi.arch=x86_64)(osgi.os=win32))", + org/xerial/snappy/native/Windows/x86/snappyjava.dll;selection-filter="(&(osgi.arch=x86)(osgi.os=win32))", + org/xerial/snappy/native/Mac/x86/libsnappyjava.jnilib;selection-filter="(&(osgi.arch=x86)(osgi.os=macosx))", + org/xerial/snappy/native/Mac/x86_64/libsnappyjava.jnilib;selection-filter="(&(osgi.arch=x86_64)(osgi.os=macosx))", + org/xerial/snappy/native/Linux/x86_64/libsnappyjava.so;selection-filter="(&(osgi.arch=x86_64)(osgi.os=linux))", + org/xerial/snappy/native/Linux/x86/libsnappyjava.so;selection-filter="(&(osgi.arch=x86)(osgi.os=linux))", + org/xerial/snappy/native/Linux/arm/libsnappyjava.so;selection-filter="(&(osgi.arch=arm)(osgi.os=linux))" +>>>>>>> feature/lib-loader diff --git a/src/main/java/org/xerial/snappy/OSInfo.java b/src/main/java/org/xerial/snappy/OSInfo.java index 4ec6686..6c612b8 100755 --- a/src/main/java/org/xerial/snappy/OSInfo.java +++ b/src/main/java/org/xerial/snappy/OSInfo.java @@ -25,6 +25,8 @@ package org.xerial.snappy; import java.io.IOException; +import java.util.HashMap; +import java.util.Locale; /** * Provides OS name and architecture name. @@ -34,6 +36,48 @@ import java.io.IOException; */ public class OSInfo { + private static HashMap archMapping = new HashMap(); + + public static final String X86 = "x86"; + public static final String X86_64 = "x86_64"; + public static final String IA64_32 = "ia64_32"; + public static final String IA64 = "ia64"; + public static final String PPC = "ppc"; + + static { + // x86 mappings + archMapping.put(X86, X86); + archMapping.put("i386", X86); + archMapping.put("i486", X86); + archMapping.put("i586", X86); + archMapping.put("i686", X86); + archMapping.put("pentium", X86); + + // x86_64 mappings + archMapping.put(X86_64, X86_64); + archMapping.put("amd64", X86_64); + archMapping.put("em64t", X86_64); + archMapping.put("universal", X86_64); // Needed for openjdk7 in Mac + + // Itenium 64-bit mappings + archMapping.put(IA64, IA64); + archMapping.put("ia64w", IA64); + + // Itenium 32-bit mappings, usually an HP-UX construct + archMapping.put(IA64_32, IA64_32); + archMapping.put("ia64n", IA64_32); + + // PowerPC mappings + archMapping.put(PPC, PPC); + archMapping.put("pwoer", PPC); + archMapping.put("powerpc", PPC); + archMapping.put("power_pc", PPC); + archMapping.put("power_rs", PPC); + + // TODO: PowerPC 64bit mappings + } + + public static void main(String[] args) { if (args.length >= 1) { if ("--os".equals(args[0])) { @@ -78,8 +122,10 @@ public class OSInfo // ignored: fall back to "arm" arch (soft-float ABI) } } - else if(getOSName().equals("Mac") && (osArch.equals("universal") || osArch.equals("amd64"))) { - return "x86_64"; // Fix for openjdk7 + else { + String lc = osArch.toLowerCase(Locale.US); + if(archMapping.containsKey(lc)) + return archMapping.get(lc); } return translateArchNameToFolderName(osArch); } diff --git a/src/main/java/org/xerial/snappy/Snappy.java b/src/main/java/org/xerial/snappy/Snappy.java index dbc29c8..bfaf175 100755 --- a/src/main/java/org/xerial/snappy/Snappy.java +++ b/src/main/java/org/xerial/snappy/Snappy.java @@ -26,7 +26,6 @@ package org.xerial.snappy; import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.lang.ExceptionInInitializerError; import java.net.URL; import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -35,7 +34,7 @@ import java.util.Properties; /** * Snappy API for data compression/decompression * - * Note: if the native libraries cannot be loaded, then an ExceptionInInitializerError + * Note: if the native libraries cannot be loaded, an ExceptionInInitializerError * will be thrown at first use of this class. * * @author leo @@ -53,9 +52,22 @@ public class Snappy } /** - * An instance of SnappyNativeAPI + * An instance of SnappyNative */ - private static Object impl; + private static SnappyNative impl; + + + /** + * Clean up a temporary file (native lib) generated by snappy-java. + * General users do not need to call this method, since the native library extracted in snappy-java + * is deleted upon JVM termination (vie deleteOnExit()). + * This method is useful when using a J2EE container, which will restart servlet containers multiple times without + * restarting JVM. + */ + static void cleanUp() { + SnappyLoader.cleanUpExtractedNativeLib(); + } + /** * Copy bytes from source to destination @@ -74,7 +86,7 @@ public class Snappy */ public static void arrayCopy(Object src, int offset, int byteLength, Object dest, int dest_offset) throws IOException { - ((SnappyNativeAPI) impl).arrayCopy(src, offset, byteLength, dest, dest_offset); + impl.arrayCopy(src, offset, byteLength, dest, dest_offset); } /** @@ -135,7 +147,7 @@ public class Snappy // output: compressed int uPos = uncompressed.position(); int uLen = uncompressed.remaining(); - int compressedSize = ((SnappyNativeAPI) impl).rawCompress(uncompressed, uPos, uLen, compressed, + int compressedSize = impl.rawCompress(uncompressed, uPos, uLen, compressed, compressed.position()); // pos limit @@ -284,7 +296,7 @@ public class Snappy public static boolean isValidCompressedBuffer(byte[] input, int offset, int length) throws IOException { if (input == null) throw new NullPointerException("input is null"); - return ((SnappyNativeAPI) impl).isValidCompressedBuffer(input, offset, length); + return impl.isValidCompressedBuffer(input, offset, length); } /** @@ -304,7 +316,7 @@ public class Snappy * factor of four faster than actual decompression. */ public static boolean isValidCompressedBuffer(ByteBuffer compressed) throws IOException { - return ((SnappyNativeAPI) impl).isValidCompressedBuffer(compressed, compressed.position(), + return impl.isValidCompressedBuffer(compressed, compressed.position(), compressed.remaining()); } @@ -317,7 +329,7 @@ public class Snappy * @return maximum byte size of the compressed data */ public static int maxCompressedLength(int byteSize) { - return ((SnappyNativeAPI) impl).maxCompressedLength(byteSize); + return impl.maxCompressedLength(byteSize); } /** @@ -329,7 +341,7 @@ public class Snappy * @throws IOException */ public static long rawCompress(long inputAddr, long inputSize, long destAddr) throws IOException { - return ((SnappyNativeAPI) impl).rawCompress(inputAddr, inputSize, destAddr); + return impl.rawCompress(inputAddr, inputSize, destAddr); } /** @@ -341,7 +353,7 @@ public class Snappy * @throws IOException */ public static long rawUncompress(long inputAddr, long inputSize, long destAddr) throws IOException { - return ((SnappyNativeAPI) impl).rawUncompress(inputAddr, inputSize, destAddr); + return impl.rawUncompress(inputAddr, inputSize, destAddr); } @@ -356,7 +368,7 @@ public class Snappy */ public static byte[] rawCompress(Object data, int byteSize) throws IOException { byte[] buf = new byte[Snappy.maxCompressedLength(byteSize)]; - int compressedByteSize = ((SnappyNativeAPI) impl).rawCompress(data, 0, byteSize, buf, 0); + int compressedByteSize = impl.rawCompress(data, 0, byteSize, buf, 0); byte[] result = new byte[compressedByteSize]; System.arraycopy(buf, 0, result, 0, compressedByteSize); return result; @@ -384,7 +396,7 @@ public class Snappy if (input == null || output == null) throw new NullPointerException("input or output is null"); - int compressedSize = ((SnappyNativeAPI) impl) + int compressedSize = impl .rawCompress(input, inputOffset, inputLength, output, outputOffset); return compressedSize; } @@ -417,7 +429,7 @@ public class Snappy throws IOException { if (input == null || output == null) throw new NullPointerException("input or output is null"); - return ((SnappyNativeAPI) impl).rawUncompress(input, inputOffset, inputLength, output, outputOffset); + return impl.rawUncompress(input, inputOffset, inputLength, output, outputOffset); } /** @@ -489,7 +501,7 @@ public class Snappy // pos limit // [ ......UUUUUU.........] - int decompressedSize = ((SnappyNativeAPI) impl).rawUncompress(compressed, cPos, cLen, uncompressed, + int decompressedSize = impl.rawUncompress(compressed, cPos, cLen, uncompressed, uncompressed.position()); uncompressed.limit(uncompressed.position() + decompressedSize); @@ -519,7 +531,7 @@ public class Snappy public static char[] uncompressCharArray(byte[] input, int offset, int length) throws IOException { int uncompressedLength = Snappy.uncompressedLength(input, offset, length); char[] result = new char[uncompressedLength / 2]; - int byteSize = ((SnappyNativeAPI) impl).rawUncompress(input, offset, length, result, 0); + int byteSize = impl.rawUncompress(input, offset, length, result, 0); return result; } @@ -533,7 +545,7 @@ public class Snappy public static double[] uncompressDoubleArray(byte[] input) throws IOException { int uncompressedLength = Snappy.uncompressedLength(input, 0, input.length); double[] result = new double[uncompressedLength / 8]; - int byteSize = ((SnappyNativeAPI) impl).rawUncompress(input, 0, input.length, result, 0); + int byteSize = impl.rawUncompress(input, 0, input.length, result, 0); return result; } @@ -548,7 +560,7 @@ public class Snappy * {@link SnappyErrorCode#PARSING_ERROR} */ public static int uncompressedLength(byte[] input) throws IOException { - return ((SnappyNativeAPI) impl).uncompressedLength(input, 0, input.length); + return impl.uncompressedLength(input, 0, input.length); } /** @@ -567,7 +579,7 @@ public class Snappy if (input == null) throw new NullPointerException("input is null"); - return ((SnappyNativeAPI) impl).uncompressedLength(input, offset, length); + return impl.uncompressedLength(input, offset, length); } /** @@ -587,7 +599,7 @@ public class Snappy if (!compressed.isDirect()) throw new SnappyError(SnappyErrorCode.NOT_A_DIRECT_BUFFER, "input is not a direct buffer"); - return ((SnappyNativeAPI) impl).uncompressedLength(compressed, compressed.position(), compressed.remaining()); + return impl.uncompressedLength(compressed, compressed.position(), compressed.remaining()); } /** @@ -599,7 +611,7 @@ public class Snappy * {@link SnappyErrorCode#PARSING_ERROR} */ public static long uncompressedLength(long inputAddr, long len) throws IOException { - return ((SnappyNativeAPI) impl).uncompressedLength(inputAddr, len); + return impl.uncompressedLength(inputAddr, len); } /** @@ -625,7 +637,7 @@ public class Snappy public static float[] uncompressFloatArray(byte[] input, int offset, int length) throws IOException { int uncompressedLength = Snappy.uncompressedLength(input, offset, length); float[] result = new float[uncompressedLength / 4]; - int byteSize = ((SnappyNativeAPI) impl).rawUncompress(input, offset, length, result, 0); + int byteSize = impl.rawUncompress(input, offset, length, result, 0); return result; } @@ -652,7 +664,7 @@ public class Snappy public static int[] uncompressIntArray(byte[] input, int offset, int length) throws IOException { int uncompressedLength = Snappy.uncompressedLength(input, offset, length); int[] result = new int[uncompressedLength / 4]; - int byteSize = ((SnappyNativeAPI) impl).rawUncompress(input, offset, length, result, 0); + int byteSize = impl.rawUncompress(input, offset, length, result, 0); return result; } @@ -679,7 +691,7 @@ public class Snappy public static long[] uncompressLongArray(byte[] input, int offset, int length) throws IOException { int uncompressedLength = Snappy.uncompressedLength(input, offset, length); long[] result = new long[uncompressedLength / 8]; - int byteSize = ((SnappyNativeAPI) impl).rawUncompress(input, offset, length, result, 0); + int byteSize = impl.rawUncompress(input, offset, length, result, 0); return result; } @@ -706,7 +718,7 @@ public class Snappy public static short[] uncompressShortArray(byte[] input, int offset, int length) throws IOException { int uncompressedLength = Snappy.uncompressedLength(input, offset, length); short[] result = new short[uncompressedLength / 2]; - int byteSize = ((SnappyNativeAPI) impl).rawUncompress(input, offset, length, result, 0); + int byteSize = impl.rawUncompress(input, offset, length, result, 0); return result; } diff --git a/src/main/java/org/xerial/snappy/SnappyBundleActivator.java b/src/main/java/org/xerial/snappy/SnappyBundleActivator.java index f36a4f5..1055f58 100755 --- a/src/main/java/org/xerial/snappy/SnappyBundleActivator.java +++ b/src/main/java/org/xerial/snappy/SnappyBundleActivator.java @@ -24,12 +24,12 @@ //-------------------------------------- package org.xerial.snappy; -import java.util.jar.Manifest; - import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import java.util.jar.Manifest; + /** * OSGi bundle entry point * @@ -56,5 +56,6 @@ public class SnappyBundleActivator implements BundleActivator public void stop(BundleContext context) throws Exception { SnappyLoader.setApi(null); + Snappy.cleanUp(); } } diff --git a/src/main/java/org/xerial/snappy/SnappyLoader.java b/src/main/java/org/xerial/snappy/SnappyLoader.java index 56f719b..0f07ae7 100755 --- a/src/main/java/org/xerial/snappy/SnappyLoader.java +++ b/src/main/java/org/xerial/snappy/SnappyLoader.java @@ -24,24 +24,11 @@ //-------------------------------------- package org.xerial.snappy; -import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.io.*; 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 @@ -94,14 +81,21 @@ public class SnappyLoader public static final String KEY_SNAPPY_DISABLE_BUNDLED_LIBS = "org.xerial.snappy.disable.bundled.libs"; // Depreciated, but preserved for backward compatibility private static volatile boolean isLoaded = false; - private static volatile Object api = null; - + private static volatile SnappyNative api = null; + + private static File nativeLibFile = null; + + static void cleanUpExtractedNativeLib() { + if(nativeLibFile != null && nativeLibFile.exists()) + nativeLibFile.delete(); + } + /** * Set the api instance. * * @param nativeCode */ - static synchronized void setApi(Object nativeCode) + static synchronized void setApi(SnappyNative nativeCode) { api = nativeCode; } @@ -142,97 +136,16 @@ 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 SnappyNative 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(); + setApi(new SnappyNative()); 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. - Object nativeCode = Class.forName("org.xerial.snappy.SnappyNative").newInstance(); - setApi(nativeCode); } catch (Exception e) { e.printStackTrace(); @@ -243,119 +156,40 @@ 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() { + private static void loadNativeLibrary() { - 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"); - - 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()); + nativeLibFile = findNativeLibrary(); + if (nativeLibFile != null) { + // Load extracted or specified snappyjava native library. + System.load(nativeLibFile.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 - * - * @param input - * @return - * @throws IOException - * @throws NoSuchAlgorithmException - */ - static String md5sum(InputStream input) throws IOException { - BufferedInputStream in = new BufferedInputStream(input); - try { - MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); - DigestInputStream digestInputStream = new DigestInputStream(in, digest); - for (; digestInputStream.read() >= 0;) { - } - ByteArrayOutputStream md5out = new ByteArrayOutputStream(); - md5out.write(digest.digest()); - return md5out.toString(); + private static boolean contentsEquals(InputStream in1, InputStream in2) throws IOException { + if(!(in1 instanceof BufferedInputStream)) { + in1 = new BufferedInputStream(in1); } - catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("MD5 algorithm is not available: " + e); + if(!(in2 instanceof BufferedInputStream)) { + in2 = new BufferedInputStream(in2); } - finally { - in.close(); + + int ch = in1.read(); + while(ch != -1) { + int ch2 = in2.read(); + if(ch != ch2) + return false; + ch = in1.read(); } + int ch2 = in2.read(); + return ch2 == -1; } - /** * Extract the specified library file to the target folder * @@ -366,40 +200,31 @@ 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; + + // 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()) { - // test md5sum value - String md5sum1 = md5sum(SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath)); - String md5sum2 = md5sum(new FileInputStream(extractedLibFile)); - - if (md5sum1.equals(md5sum2)) { - 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()); - } - } - } // Extract a native library file into the target directory InputStream reader = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath); FileOutputStream writer = new FileOutputStream(extractedLibFile); - byte[] buffer = new byte[8192]; - int bytesRead = 0; - while ((bytesRead = reader.read(buffer)) != -1) { - writer.write(buffer, 0, bytesRead); + try { + byte[] buffer = new byte[8192]; + int bytesRead = 0; + while ((bytesRead = reader.read(buffer)) != -1) { + writer.write(buffer, 0, bytesRead); + } + } + finally { + if(writer != null) + writer.close(); + if(reader != null) + reader.close(); } - - writer.close(); - reader.close(); // Set executable (x) flag to enable Java to load the native library if (!System.getProperty("os.name").contains("Windows")) { @@ -410,6 +235,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) { @@ -460,7 +301,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(); @@ -475,8 +316,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 diff --git a/src/main/java/org/xerial/snappy/SnappyNative.java b/src/main/java/org/xerial/snappy/SnappyNative.java index fdcb617..7e9dbb2 100755 --- a/src/main/java/org/xerial/snappy/SnappyNative.java +++ b/src/main/java/org/xerial/snappy/SnappyNative.java @@ -28,8 +28,7 @@ import java.io.IOException; import java.nio.ByteBuffer; /** - * Internal only - Do not use this class. JNI interface of the - * {@link SnappyNativeAPI} implementation. The native method in this class is + * JNI interface of the {@link Snappy} implementation. The native method in this class is * defined in SnappyNative.h (genereted by javah) and SnappyNative.cpp * *

@@ -40,7 +39,7 @@ import java.nio.ByteBuffer; * @author leo * */ -public class SnappyNative implements SnappyNativeAPI +public class SnappyNative { public native String nativeLibraryVersion(); diff --git a/src/main/java/org/xerial/snappy/SnappyNativeAPI.java b/src/main/java/org/xerial/snappy/SnappyNativeAPI.java deleted file mode 100755 index b6c92d0..0000000 --- a/src/main/java/org/xerial/snappy/SnappyNativeAPI.java +++ /dev/null @@ -1,82 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright 2011 Taro L. Saito - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *--------------------------------------------------------------------------*/ -//-------------------------------------- -// snappy-java Project -// -// SnappyNative.java -// Since: 2011/03/30 -// -// $URL$ -// $Author$ -//-------------------------------------- -package org.xerial.snappy; - -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * Internal only - Do not use this class. - * - * Interface to access the native code of Snappy. Although this class members - * are public, do not use them directly. Use {@link Snappy} API instead. - * - * - * @author leo - * - */ -public interface SnappyNativeAPI -{ - - public String nativeLibraryVersion(); - - // ------------------------------------------------------------------------ - // Generic compression/decompression routines. - // ------------------------------------------------------------------------ - public long rawCompress(long inputAddr, long inputSize, long destAddr) throws IOException; - public long rawUncompress(long inputAddr, long inputSize, long destAddr) throws IOException; - - public int rawCompress(ByteBuffer input, int inputOffset, int inputLength, ByteBuffer compressed, int outputOffset) - throws IOException; - - public int rawCompress(Object input, int inputOffset, int inputByteLength, Object output, int outputOffset) throws IOException; - - public int rawUncompress(ByteBuffer compressed, int inputOffset, int inputLength, ByteBuffer uncompressed, - int outputOffset) throws IOException; - - public int rawUncompress(Object input, int inputOffset, int inputLength, Object output, int outputOffset) - throws IOException; - - // Returns the maximal size of the compressed representation of - // input data that is "source_bytes" bytes in length; - public int maxCompressedLength(int source_bytes); - - // This operation takes O(1) time. - public int uncompressedLength(ByteBuffer compressed, int offset, int len) throws IOException; - - public int uncompressedLength(Object input, int offset, int len) throws IOException; - - public long uncompressedLength(long inputAddr, long len) throws IOException; - - - public boolean isValidCompressedBuffer(ByteBuffer compressed, int offset, int len) throws IOException; - - public boolean isValidCompressedBuffer(Object input, int offset, int len) throws IOException; - - public void arrayCopy(Object src, int offset, int byteLength, Object dest, int dOffset) throws IOException; - - public void throw_error(int errorCode) throws IOException; - -} diff --git a/src/main/resources/org/xerial/snappy/native/Linux/i386/libsnappyjava.so b/src/main/resources/org/xerial/snappy/native/Linux/x86/libsnappyjava.so similarity index 100% rename from src/main/resources/org/xerial/snappy/native/Linux/i386/libsnappyjava.so rename to src/main/resources/org/xerial/snappy/native/Linux/x86/libsnappyjava.so diff --git a/src/main/resources/org/xerial/snappy/native/Linux/amd64/libsnappyjava.so b/src/main/resources/org/xerial/snappy/native/Linux/x86_64/libsnappyjava.so similarity index 100% rename from src/main/resources/org/xerial/snappy/native/Linux/amd64/libsnappyjava.so rename to src/main/resources/org/xerial/snappy/native/Linux/x86_64/libsnappyjava.so diff --git a/src/main/resources/org/xerial/snappy/native/Mac/i386/libsnappyjava.jnilib b/src/main/resources/org/xerial/snappy/native/Mac/x86/libsnappyjava.jnilib similarity index 100% rename from src/main/resources/org/xerial/snappy/native/Mac/i386/libsnappyjava.jnilib rename to src/main/resources/org/xerial/snappy/native/Mac/x86/libsnappyjava.jnilib diff --git a/src/main/resources/org/xerial/snappy/native/OpenBSD/i386/libsnappyjava.so b/src/main/resources/org/xerial/snappy/native/OpenBSD/x86/libsnappyjava.so similarity index 100% rename from src/main/resources/org/xerial/snappy/native/OpenBSD/i386/libsnappyjava.so rename to src/main/resources/org/xerial/snappy/native/OpenBSD/x86/libsnappyjava.so diff --git a/src/main/resources/org/xerial/snappy/native/OpenBSD/amd64/libsnappyjava.so b/src/main/resources/org/xerial/snappy/native/OpenBSD/x86_64/libsnappyjava.so similarity index 100% rename from src/main/resources/org/xerial/snappy/native/OpenBSD/amd64/libsnappyjava.so rename to src/main/resources/org/xerial/snappy/native/OpenBSD/x86_64/libsnappyjava.so diff --git a/src/main/resources/org/xerial/snappy/native/Windows/amd64/snappyjava.dll b/src/main/resources/org/xerial/snappy/native/Windows/x86_64/snappyjava.dll similarity index 100% rename from src/main/resources/org/xerial/snappy/native/Windows/amd64/snappyjava.dll rename to src/main/resources/org/xerial/snappy/native/Windows/x86_64/snappyjava.dll diff --git a/src/test/java/org/xerial/snappy/SnappyLoaderTest.java b/src/test/java/org/xerial/snappy/SnappyLoaderTest.java index 81ecd53..9bcc85f 100755 --- a/src/test/java/org/xerial/snappy/SnappyLoaderTest.java +++ b/src/test/java/org/xerial/snappy/SnappyLoaderTest.java @@ -24,23 +24,20 @@ //-------------------------------------- package org.xerial.snappy; -import static org.junit.Assert.*; - -import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; - import org.codehaus.plexus.classworlds.ClassWorld; import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.junit.Test; import org.xerial.util.FileResource; import org.xerial.util.log.Logger; +import java.io.*; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.fail; + public class SnappyLoaderTest { private static Logger _logger = Logger.getLogger(SnappyLoaderTest.class);