Add a trick to delegate native library loading to the parent class loader

This commit is contained in:
Taro L. Saito 2011-06-23 22:09:09 +09:00
parent 4301cfd9bd
commit dc0e8a3150
10 changed files with 279 additions and 116 deletions

View File

@ -232,5 +232,12 @@
<type>jar</type> <type>jar</type>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.14.0-GA</version>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -24,6 +24,7 @@
//-------------------------------------- //--------------------------------------
package org.xerial.snappy; package org.xerial.snappy;
import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -49,9 +50,9 @@ public class Snappy
* @param input * @param input
* the input data * the input data
* @return the compressed byte array * @return the compressed byte array
* @throws SnappyException * @throws IOException
*/ */
public static byte[] compress(byte[] input) throws SnappyException { public static byte[] compress(byte[] input) throws IOException {
return rawCompress(input, input.length); return rawCompress(input, input.length);
} }
@ -65,11 +66,11 @@ public class Snappy
* @param output * @param output
* @param outputOffset * @param outputOffset
* @return byte size of the compressed data * @return byte size of the compressed data
* @throws SnappyException * @throws IOException
* when failed to access the input/output buffer * when failed to access the input/output buffer
*/ */
public static int compress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset) public static int compress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)
throws SnappyException { throws IOException {
return rawCompress(input, inputOffset, inputLength, output, outputOffset); return rawCompress(input, inputOffset, inputLength, output, outputOffset);
} }
@ -87,7 +88,7 @@ public class Snappy
* @throws SnappyError * @throws SnappyError
* when the input is not a direct buffer * when the input is not a direct buffer
*/ */
public static int compress(ByteBuffer uncompressed, ByteBuffer compressed) throws SnappyException { public static int compress(ByteBuffer uncompressed, ByteBuffer compressed) throws IOException {
if (!uncompressed.isDirect()) if (!uncompressed.isDirect())
throw new SnappyError(SnappyErrorCode.NOT_A_DIRECT_BUFFER, "input is not a direct buffer"); throw new SnappyError(SnappyErrorCode.NOT_A_DIRECT_BUFFER, "input is not a direct buffer");
@ -131,7 +132,7 @@ public class Snappy
return rawCompress(input, input.length * 2); // short uses 2 bytes return rawCompress(input, input.length * 2); // short uses 2 bytes
} }
public static byte[] compress(String s) throws SnappyException { public static byte[] compress(String s) throws IOException {
try { try {
return compress(s, "UTF-8"); return compress(s, "UTF-8");
} }
@ -140,7 +141,7 @@ public class Snappy
} }
} }
public static byte[] compress(String s, String encoding) throws UnsupportedEncodingException, SnappyException { public static byte[] compress(String s, String encoding) throws UnsupportedEncodingException, IOException {
byte[] data = s.getBytes(encoding); byte[] data = s.getBytes(encoding);
return compress(data); return compress(data);
} }
@ -160,7 +161,7 @@ public class Snappy
* uncompressed data. Takes time proportional to the input length, but is * uncompressed data. Takes time proportional to the input length, but is
* usually at least a factor of four faster than actual decompression. * usually at least a factor of four faster than actual decompression.
*/ */
public static boolean isValidCompressedBuffer(byte[] input, int offset, int length) throws SnappyException { public static boolean isValidCompressedBuffer(byte[] input, int offset, int length) throws IOException {
if (input == null) if (input == null)
throw new NullPointerException("input is null"); throw new NullPointerException("input is null");
return SnappyNative.isValidCompressedBuffer(input, offset, length); return SnappyNative.isValidCompressedBuffer(input, offset, length);
@ -172,7 +173,7 @@ public class Snappy
* uncompressed data. Takes time proportional to the input length, but is * uncompressed data. Takes time proportional to the input length, but is
* usually at least a factor of four faster than actual decompression. * usually at least a factor of four faster than actual decompression.
*/ */
public static boolean isValidCompressedBuffer(byte[] input) throws SnappyException { public static boolean isValidCompressedBuffer(byte[] input) throws IOException {
return isValidCompressedBuffer(input, 0, input.length); return isValidCompressedBuffer(input, 0, input.length);
} }
@ -182,7 +183,7 @@ public class Snappy
* Takes time proportional to the input length, but is usually at least a * Takes time proportional to the input length, but is usually at least a
* factor of four faster than actual decompression. * factor of four faster than actual decompression.
*/ */
public static boolean isValidCompressedBuffer(ByteBuffer compressed) throws SnappyException { public static boolean isValidCompressedBuffer(ByteBuffer compressed) throws IOException {
return SnappyNative.isValidCompressedBuffer(compressed, compressed.position(), compressed.remaining()); return SnappyNative.isValidCompressedBuffer(compressed, compressed.position(), compressed.remaining());
} }
@ -230,10 +231,10 @@ public class Snappy
* @param outputOffset * @param outputOffset
* byte offset at the output array * byte offset at the output array
* @return byte size of the compressed data * @return byte size of the compressed data
* @throws SnappyException * @throws IOException
*/ */
public static int rawCompress(Object input, int inputOffset, int inputLength, byte[] output, int outputOffset) public static int rawCompress(Object input, int inputOffset, int inputLength, byte[] output, int outputOffset)
throws SnappyException { throws IOException {
if (input == null || output == null) if (input == null || output == null)
throw new NullPointerException("input or output is null"); throw new NullPointerException("input or output is null");
@ -262,10 +263,10 @@ public class Snappy
* @param outputOffset * @param outputOffset
* byte offset * byte offset
* @return the byte size of the uncompressed data * @return the byte size of the uncompressed data
* @throws SnappyException * @throws IOException
*/ */
public static int rawUncompress(byte[] input, int inputOffset, int inputLength, Object output, int outputOffset) public static int rawUncompress(byte[] input, int inputOffset, int inputLength, Object output, int outputOffset)
throws SnappyException { throws IOException {
if (input == null || output == null) if (input == null || output == null)
throw new NullPointerException("input or output is null"); throw new NullPointerException("input or output is null");
return SnappyNative.rawUncompress(input, inputOffset, inputLength, output, outputOffset); return SnappyNative.rawUncompress(input, inputOffset, inputLength, output, outputOffset);
@ -276,9 +277,9 @@ public class Snappy
* *
* @param input * @param input
* @return the uncompressed byte array * @return the uncompressed byte array
* @throws SnappyException * @throws IOException
*/ */
public static byte[] uncompress(byte[] input) throws SnappyException { public static byte[] uncompress(byte[] input) throws IOException {
byte[] result = new byte[Snappy.uncompressedLength(input)]; byte[] result = new byte[Snappy.uncompressedLength(input)];
int byteSize = Snappy.uncompress(input, 0, input.length, result, 0); int byteSize = Snappy.uncompress(input, 0, input.length, result, 0);
return result; return result;
@ -300,10 +301,10 @@ public class Snappy
* @param output * @param output
* @param outputOffset * @param outputOffset
* @return the byte size of the uncompressed data * @return the byte size of the uncompressed data
* @throws SnappyException * @throws IOException
*/ */
public static int uncompress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset) public static int uncompress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)
throws SnappyException { throws IOException {
return rawUncompress(input, inputOffset, inputLength, output, outputOffset); return rawUncompress(input, inputOffset, inputLength, output, outputOffset);
} }
@ -323,12 +324,12 @@ public class Snappy
* output of the the uncompressed data. It uses buffer[pot()..] * output of the the uncompressed data. It uses buffer[pot()..]
* @return uncompressed data size * @return uncompressed data size
* *
* @throws SnappyException * @throws IOException
* when failed to uncompress the given input * when failed to uncompress the given input
* @throws SnappyError * @throws SnappyError
* when the input is not a direct buffer * when the input is not a direct buffer
*/ */
public static int uncompress(ByteBuffer compressed, ByteBuffer uncompressed) throws SnappyException { public static int uncompress(ByteBuffer compressed, ByteBuffer uncompressed) throws IOException {
if (!compressed.isDirect()) if (!compressed.isDirect())
throw new SnappyError(SnappyErrorCode.NOT_A_DIRECT_BUFFER, "input is not a direct buffer"); throw new SnappyError(SnappyErrorCode.NOT_A_DIRECT_BUFFER, "input is not a direct buffer");
@ -347,18 +348,18 @@ public class Snappy
return decompressedSize; return decompressedSize;
} }
public static char[] uncompressCharArray(byte[] input) throws SnappyException { public static char[] uncompressCharArray(byte[] input) throws IOException {
return uncompressCharArray(input, 0, input.length); return uncompressCharArray(input, 0, input.length);
} }
public static char[] uncompressCharArray(byte[] input, int offset, int length) throws SnappyException { public static char[] uncompressCharArray(byte[] input, int offset, int length) throws IOException {
int uncompressedLength = Snappy.uncompressedLength(input, offset, length); int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
char[] result = new char[uncompressedLength / 2]; char[] result = new char[uncompressedLength / 2];
int byteSize = SnappyNative.rawUncompress(input, offset, length, result, 0); int byteSize = SnappyNative.rawUncompress(input, offset, length, result, 0);
return result; return result;
} }
public static double[] uncompressDoubleArray(byte[] input) throws SnappyException { public static double[] uncompressDoubleArray(byte[] input) throws IOException {
int uncompressedLength = Snappy.uncompressedLength(input, 0, input.length); int uncompressedLength = Snappy.uncompressedLength(input, 0, input.length);
double[] result = new double[uncompressedLength / 8]; double[] result = new double[uncompressedLength / 8];
int byteSize = SnappyNative.rawUncompress(input, 0, input.length, result, 0); int byteSize = SnappyNative.rawUncompress(input, 0, input.length, result, 0);
@ -371,11 +372,11 @@ public class Snappy
* *
* @param input * @param input
* @return umcompressed byte size of the the given input data * @return umcompressed byte size of the the given input data
* @throws SnappyException * @throws IOException
* when failed to uncompress the given input. The error code is * when failed to uncompress the given input. The error code is
* {@link SnappyErrorCode#PARSING_ERROR} * {@link SnappyErrorCode#PARSING_ERROR}
*/ */
public static int uncompressedLength(byte[] input) throws SnappyException { public static int uncompressedLength(byte[] input) throws IOException {
return SnappyNative.uncompressedLength(input, 0, input.length); return SnappyNative.uncompressedLength(input, 0, input.length);
} }
@ -387,11 +388,11 @@ public class Snappy
* @param offset * @param offset
* @param length * @param length
* @return umcompressed byte size of the the given input data * @return umcompressed byte size of the the given input data
* @throws SnappyException * @throws IOException
* when failed to uncompress the given input. The error code is * when failed to uncompress the given input. The error code is
* {@link SnappyErrorCode#PARSING_ERROR} * {@link SnappyErrorCode#PARSING_ERROR}
*/ */
public static int uncompressedLength(byte[] input, int offset, int length) throws SnappyException { public static int uncompressedLength(byte[] input, int offset, int length) throws IOException {
if (input == null) if (input == null)
throw new NullPointerException("input is null"); throw new NullPointerException("input is null");
@ -409,8 +410,7 @@ public class Snappy
} }
} }
public static CompressedDataLength getUncompressedLength(byte[] input, int offset, int limit) public static CompressedDataLength getUncompressedLength(byte[] input, int offset, int limit) throws IOException {
throws SnappyException {
if (input == null) if (input == null)
throw new NullPointerException("input is null"); throw new NullPointerException("input is null");
@ -463,64 +463,64 @@ public class Snappy
* @param compressed * @param compressed
* input data [pos() ... limit()) * input data [pos() ... limit())
* @return uncompressed byte length of the given input * @return uncompressed byte length of the given input
* @throws SnappyException * @throws IOException
* when failed to uncompress the given input. The error code is * when failed to uncompress the given input. The error code is
* {@link SnappyErrorCode#PARSING_ERROR} * {@link SnappyErrorCode#PARSING_ERROR}
* @throws SnappyError * @throws SnappyError
* when the input is not a direct buffer * when the input is not a direct buffer
*/ */
public static int uncompressedLength(ByteBuffer compressed) throws SnappyException { public static int uncompressedLength(ByteBuffer compressed) throws IOException {
if (!compressed.isDirect()) if (!compressed.isDirect())
throw new SnappyError(SnappyErrorCode.NOT_A_DIRECT_BUFFER, "input is not a direct buffer"); throw new SnappyError(SnappyErrorCode.NOT_A_DIRECT_BUFFER, "input is not a direct buffer");
return SnappyNative.uncompressedLength(compressed, compressed.position(), compressed.remaining()); return SnappyNative.uncompressedLength(compressed, compressed.position(), compressed.remaining());
} }
public static float[] uncompressFloatArray(byte[] input) throws SnappyException { public static float[] uncompressFloatArray(byte[] input) throws IOException {
return uncompressFloatArray(input, 0, input.length); return uncompressFloatArray(input, 0, input.length);
} }
public static float[] uncompressFloatArray(byte[] input, int offset, int length) throws SnappyException { public static float[] uncompressFloatArray(byte[] input, int offset, int length) throws IOException {
int uncompressedLength = Snappy.uncompressedLength(input, offset, length); int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
float[] result = new float[uncompressedLength / 4]; float[] result = new float[uncompressedLength / 4];
int byteSize = SnappyNative.rawUncompress(input, offset, length, result, 0); int byteSize = SnappyNative.rawUncompress(input, offset, length, result, 0);
return result; return result;
} }
public static int[] uncompressIntArray(byte[] input) throws SnappyException { public static int[] uncompressIntArray(byte[] input) throws IOException {
return uncompressIntArray(input, 0, input.length); return uncompressIntArray(input, 0, input.length);
} }
public static int[] uncompressIntArray(byte[] input, int offset, int length) throws SnappyException { public static int[] uncompressIntArray(byte[] input, int offset, int length) throws IOException {
int uncompressedLength = Snappy.uncompressedLength(input, offset, length); int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
int[] result = new int[uncompressedLength / 4]; int[] result = new int[uncompressedLength / 4];
int byteSize = SnappyNative.rawUncompress(input, offset, length, result, 0); int byteSize = SnappyNative.rawUncompress(input, offset, length, result, 0);
return result; return result;
} }
public static long[] uncompressLongArray(byte[] input) throws SnappyException { public static long[] uncompressLongArray(byte[] input) throws IOException {
return uncompressLongArray(input, 0, input.length); return uncompressLongArray(input, 0, input.length);
} }
public static long[] uncompressLongArray(byte[] input, int offset, int length) throws SnappyException { public static long[] uncompressLongArray(byte[] input, int offset, int length) throws IOException {
int uncompressedLength = Snappy.uncompressedLength(input, offset, length); int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
long[] result = new long[uncompressedLength / 8]; long[] result = new long[uncompressedLength / 8];
int byteSize = SnappyNative.rawUncompress(input, offset, length, result, 0); int byteSize = SnappyNative.rawUncompress(input, offset, length, result, 0);
return result; return result;
} }
public static short[] uncompressShortArray(byte[] input) throws SnappyException { public static short[] uncompressShortArray(byte[] input) throws IOException {
return uncompressShortArray(input, 0, input.length); return uncompressShortArray(input, 0, input.length);
} }
public static short[] uncompressShortArray(byte[] input, int offset, int length) throws SnappyException { public static short[] uncompressShortArray(byte[] input, int offset, int length) throws IOException {
int uncompressedLength = Snappy.uncompressedLength(input, offset, length); int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
short[] result = new short[uncompressedLength / 2]; short[] result = new short[uncompressedLength / 2];
int byteSize = SnappyNative.rawUncompress(input, offset, length, result, 0); int byteSize = SnappyNative.rawUncompress(input, offset, length, result, 0);
return result; return result;
} }
public static String uncompressString(byte[] input) throws SnappyException { public static String uncompressString(byte[] input) throws IOException {
try { try {
return uncompressString(input, "UTF-8"); return uncompressString(input, "UTF-8");
} }
@ -529,7 +529,7 @@ public class Snappy
} }
} }
public static String uncompressString(byte[] input, int offset, int length) throws SnappyException { public static String uncompressString(byte[] input, int offset, int length) throws IOException {
try { try {
return uncompressString(input, offset, length, "UTF-8"); return uncompressString(input, offset, length, "UTF-8");
} }
@ -538,14 +538,14 @@ public class Snappy
} }
} }
public static String uncompressString(byte[] input, int offset, int length, String encoding) public static String uncompressString(byte[] input, int offset, int length, String encoding) throws IOException,
throws SnappyException, UnsupportedEncodingException { UnsupportedEncodingException {
byte[] uncompressed = new byte[uncompressedLength(input, offset, length)]; byte[] uncompressed = new byte[uncompressedLength(input, offset, length)];
int compressedSize = uncompress(input, offset, length, uncompressed, 0); int compressedSize = uncompress(input, offset, length, uncompressed, 0);
return new String(uncompressed, encoding); return new String(uncompressed, encoding);
} }
public static String uncompressString(byte[] input, String encoding) throws SnappyException, public static String uncompressString(byte[] input, String encoding) throws IOException,
UnsupportedEncodingException { UnsupportedEncodingException {
byte[] uncompressed = uncompress(input); byte[] uncompressed = uncompress(input);
return new String(uncompressed, encoding); return new String(uncompressed, encoding);

View File

@ -45,4 +45,12 @@ public enum SnappyErrorCode {
private SnappyErrorCode(int id) { private SnappyErrorCode(int id) {
this.id = id; this.id = id;
} }
public static String getErrorMessage(int id) {
for (SnappyErrorCode code : SnappyErrorCode.values()) {
if (code.id == id)
return code.name();
}
return UNKNOWN.name();
}
} }

View File

@ -95,16 +95,11 @@ public class SnappyInputStream extends InputStream
finishedReading = true; finishedReading = true;
// Uncompress // Uncompress
try { int uncompressedLength = Snappy.uncompressedLength(compressed, 0, cursor);
int uncompressedLength = Snappy.uncompressedLength(compressed, 0, cursor); uncompressed = new byte[uncompressedLength];
uncompressed = new byte[uncompressedLength]; Snappy.uncompress(compressed, 0, cursor, uncompressed, 0);
Snappy.uncompress(compressed, 0, cursor, uncompressed, 0); this.uncompressedCursor = 0;
this.uncompressedCursor = 0; this.uncompressedLimit = uncompressedLength;
this.uncompressedLimit = uncompressedLength;
}
catch (SnappyException e) {
throw new IOException(e.getMessage());
}
} }
@ -160,7 +155,7 @@ public class SnappyInputStream extends InputStream
} }
uncompressedLimit = actualUncompressedLength; uncompressedLimit = actualUncompressedLength;
} }
catch (SnappyException e) { catch (IOException e) {
throw new IOException("failed to uncompress the chunk: " + e.getMessage()); throw new IOException("failed to uncompress the chunk: " + e.getMessage());
} }

View File

@ -35,6 +35,9 @@ 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.List;
import java.util.Properties; import java.util.Properties;
/** /**
@ -77,13 +80,97 @@ import java.util.Properties;
*/ */
public class SnappyLoader public class SnappyLoader
{ {
private static boolean isLoaded = false; private static boolean isInitialized = false;
private static boolean isLoaded = false;
private static ClassLoader getSystemClassLoader() {
ClassLoader cl = SnappyLoader.class.getClassLoader();
while (cl.getParent() != null) {
cl = cl.getParent();
}
return cl;
}
private static byte[] getByteCode(String resourcePath) throws IOException {
InputStream in = SnappyLoader.class.getResourceAsStream(resourcePath);
assert (in != null);
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 void load() {
if (!isInitialized) {
final String nativeLoaderClassName = "org.xerial.snappy.SnappyNativeLoader";
final String[] preloadClass = new String[] { "org.xerial.snappy.SnappyNative",
"org.xerial.snappy.SnappyErrorCode" };
try {
Class.forName(nativeLoaderClassName);
// If this native loader class is already defined, it means that another class loader already loaded the native library of snappy
isInitialized = true;
return;
}
catch (ClassNotFoundException e) {
try {
// Load a byte code
byte[] byteCode = getByteCode("/org/xerial/snappy/SnappyNativeLoader.bytecode");
// In addition, load the SnappyNative and SnappyException class in the system class loader
List<byte[]> preloadClassByteCode = new ArrayList<byte[]>(preloadClass.length);
for (String each : preloadClass) {
preloadClassByteCode.add(getByteCode(String.format("/%s.class", each.replaceAll("\\.", "/"))));
}
// Create a new class to the system class loader
Class< ? > classLoader = Class.forName("java.lang.ClassLoader");
java.lang.reflect.Method defineClass = classLoader.getDeclaredMethod("defineClass", new Class[] {
String.class, byte[].class, int.class, int.class, ProtectionDomain.class });
ClassLoader systemClassLoader = getSystemClassLoader();
defineClass.setAccessible(true);
try {
// Load SnappyNativeLoader
defineClass.invoke(systemClassLoader, nativeLoaderClassName, byteCode, 0, byteCode.length,
System.class.getProtectionDomain());
for (int i = 0; i < preloadClass.length; ++i) {
byte[] b = preloadClassByteCode.get(i);
defineClass.invoke(systemClassLoader, preloadClass[i], b, 0, b.length,
System.class.getProtectionDomain());
}
}
finally {
defineClass.setAccessible(false);
}
// Load the loader class
Class< ? > loaderClass = systemClassLoader.loadClass(nativeLoaderClassName);
if (loaderClass != null) {
java.lang.reflect.Method loadMethod = loaderClass.getDeclaredMethod("load",
new Class[] { String.class });
File nativeLib = findNativeLibrary();
loadMethod.invoke(null, nativeLib.getAbsolutePath());
for (String each : preloadClass) {
systemClassLoader.loadClass(each);
}
isInitialized = true;
}
}
catch (Exception e2) {
e.printStackTrace();
}
}
public static boolean load() {
if (!isLoaded) {
loadSnappyNativeLibrary();
} }
return isLoaded;
} }
public static final String KEY_SNAPPY_LIB_PATH = "org.xerial.snappy.lib.path"; public static final String KEY_SNAPPY_LIB_PATH = "org.xerial.snappy.lib.path";
@ -101,7 +188,6 @@ public class SnappyLoader
*/ */
static String md5sum(InputStream input) throws IOException { static String md5sum(InputStream input) throws IOException {
BufferedInputStream in = new BufferedInputStream(input); BufferedInputStream in = new BufferedInputStream(input);
try { try {
MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
DigestInputStream digestInputStream = new DigestInputStream(in, digest); DigestInputStream digestInputStream = new DigestInputStream(in, digest);
@ -128,8 +214,7 @@ public class SnappyLoader
* @param targetFolder * @param targetFolder
* @return * @return
*/ */
private static boolean extractAndLoadLibraryFile(String libFolderForCurrentOS, String libraryFileName, private static File extractLibraryFile(String libFolderForCurrentOS, String libraryFileName, String targetFolder) {
String targetFolder) {
String nativeLibraryFilePath = libFolderForCurrentOS + "/" + libraryFileName; String nativeLibraryFilePath = libFolderForCurrentOS + "/" + libraryFileName;
final String prefix = "snappy-" + getVersion() + "-"; final String prefix = "snappy-" + getVersion() + "-";
String extractedLibFileName = prefix + libraryFileName; String extractedLibFileName = prefix + libraryFileName;
@ -142,7 +227,7 @@ public class SnappyLoader
String md5sum2 = md5sum(new FileInputStream(extractedLibFile)); String md5sum2 = md5sum(new FileInputStream(extractedLibFile));
if (md5sum1.equals(md5sum2)) { if (md5sum1.equals(md5sum2)) {
return loadNativeLibrary(targetFolder, extractedLibFileName); return new File(targetFolder, extractedLibFileName);
} }
else { else {
// remove old native library file // remove old native library file
@ -154,10 +239,10 @@ public class SnappyLoader
} }
} }
// extract a native library file into the target directory // Extract a native library file into the target directory
InputStream reader = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath); InputStream reader = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
FileOutputStream writer = new FileOutputStream(extractedLibFile); FileOutputStream writer = new FileOutputStream(extractedLibFile);
byte[] buffer = new byte[1024]; byte[] buffer = new byte[8192];
int bytesRead = 0; int bytesRead = 0;
while ((bytesRead = reader.read(buffer)) != -1) { while ((bytesRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, bytesRead); writer.write(buffer, 0, bytesRead);
@ -175,52 +260,46 @@ public class SnappyLoader
catch (Throwable e) {} catch (Throwable e) {}
} }
return loadNativeLibrary(targetFolder, extractedLibFileName); return new File(targetFolder, extractedLibFileName);
} }
catch (IOException e) { catch (IOException e) {
System.err.println(e.getMessage()); e.printStackTrace(System.err);
return false; return null;
} }
} }
private static synchronized boolean loadNativeLibrary(String path, String name) { private static synchronized boolean loadNativeLibrary(File libPath) {
File libPath = new File(path, name);
if (libPath.exists()) { if (libPath.exists()) {
try { try {
System.load(new File(path, name).getAbsolutePath()); System.load(libPath.getAbsolutePath());
return true; return true;
} }
catch (UnsatisfiedLinkError e) { catch (UnsatisfiedLinkError e) {
throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, e); throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, e);
} }
} }
else else
return false; return false;
} }
private static void loadSnappyNativeLibrary() { static File findNativeLibrary() {
if (isLoaded) // Try to load the library from org.xerial.snappy.lib.path library path */
return; String snappyNativeLibraryPath = System.getProperty(KEY_SNAPPY_LIB_PATH);
String snappyNativeLibraryName = System.getProperty(KEY_SNAPPY_LIB_NAME);
if (System.getProperty(KEY_SNAPPY_DISABLE_BUNDLED_LIBS, "false").equals("false")) { if (System.getProperty(KEY_SNAPPY_DISABLE_BUNDLED_LIBS, "false").equals("false")) {
// Try to load the library from org.xerial.snappy.lib.path library path */
String snappyNativeLibraryPath = System.getProperty(KEY_SNAPPY_LIB_PATH);
String snappyNativeLibraryName = System.getProperty(KEY_SNAPPY_LIB_NAME);
// Resolve the library file name with a suffix (e.g., dll, .so, etc.) // Resolve the library file name with a suffix (e.g., dll, .so, etc.)
if (snappyNativeLibraryName == null) if (snappyNativeLibraryName == null)
snappyNativeLibraryName = System.mapLibraryName("snappyjava"); snappyNativeLibraryName = System.mapLibraryName("snappyjava");
if (snappyNativeLibraryPath != null) { if (snappyNativeLibraryPath != null) {
if (loadNativeLibrary(snappyNativeLibraryPath, snappyNativeLibraryName)) { File nativeLib = new File(snappyNativeLibraryPath, snappyNativeLibraryName);
isLoaded = true; if (nativeLib.exists())
return; return nativeLib;
}
} }
}
{
// Load an OS-dependent native library inside a jar file // Load an OS-dependent native library inside a jar file
snappyNativeLibraryPath = "/org/xerial/snappy/native/" + OSInfo.getNativeLibFolderPathForCurrentOS(); snappyNativeLibraryPath = "/org/xerial/snappy/native/" + OSInfo.getNativeLibFolderPathForCurrentOS();
@ -230,10 +309,22 @@ public class SnappyLoader
System.getProperty("java.io.tmpdir"))).getAbsolutePath(); System.getProperty("java.io.tmpdir"))).getAbsolutePath();
// Extract and load a native library inside the jar file // Extract and load a native library inside the jar file
if (extractAndLoadLibraryFile(snappyNativeLibraryPath, snappyNativeLibraryName, tempFolder)) { return extractLibraryFile(snappyNativeLibraryPath, snappyNativeLibraryName, tempFolder);
isLoaded = true; }
return; }
}
return null;
}
private static void loadSnappyNativeLibrary() {
if (isLoaded)
return;
File nativeLibrary = findNativeLibrary();
if (nativeLibrary != null) {
if (loadNativeLibrary(nativeLibrary)) {
isLoaded = true;
return;
} }
} }

View File

@ -24,6 +24,7 @@
//-------------------------------------- //--------------------------------------
package org.xerial.snappy; package org.xerial.snappy;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
@ -35,37 +36,38 @@ import java.nio.ByteBuffer;
public class SnappyNative public class SnappyNative
{ {
native static String nativeLibraryVersion(); public native static String nativeLibraryVersion();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Generic compression/decompression routines. // Generic compression/decompression routines.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
native static int rawCompress(ByteBuffer input, int inputOffset, int inputLength, ByteBuffer compressed, public native static int rawCompress(ByteBuffer input, int inputOffset, int inputLength, ByteBuffer compressed,
int outputOffset) throws SnappyException; int outputOffset) throws IOException;
native static int rawCompress(Object input, int inputOffset, int inputByteLength, Object output, int outputOffset); public native static int rawCompress(Object input, int inputOffset, int inputByteLength, Object output,
int outputOffset);
native static int rawUncompress(ByteBuffer compressed, int inputOffset, int inputLength, ByteBuffer uncompressed, public native static int rawUncompress(ByteBuffer compressed, int inputOffset, int inputLength,
int outputOffset) throws SnappyException; ByteBuffer uncompressed, int outputOffset) throws IOException;
native static int rawUncompress(Object input, int inputOffset, int inputLength, Object output, int outputOffset) public native static int rawUncompress(Object input, int inputOffset, int inputLength, Object output,
throws SnappyException; int outputOffset) throws IOException;
// Returns the maximal size of the compressed representation of // Returns the maximal size of the compressed representation of
// input data that is "source_bytes" bytes in length; // input data that is "source_bytes" bytes in length;
native static int maxCompressedLength(int source_bytes); public native static int maxCompressedLength(int source_bytes);
// This operation takes O(1) time. // This operation takes O(1) time.
native static int uncompressedLength(ByteBuffer compressed, int offset, int len) throws SnappyException; public native static int uncompressedLength(ByteBuffer compressed, int offset, int len) throws IOException;
native static int uncompressedLength(Object input, int offset, int len) throws SnappyException; public native static int uncompressedLength(Object input, int offset, int len) throws IOException;
native static boolean isValidCompressedBuffer(ByteBuffer compressed, int offset, int len) throws SnappyException; public native static boolean isValidCompressedBuffer(ByteBuffer compressed, int offset, int len) throws IOException;
native static boolean isValidCompressedBuffer(Object input, int offset, int len) throws SnappyException; public native static boolean isValidCompressedBuffer(Object input, int offset, int len) throws IOException;
protected static void throw_error(int errorCode) throws SnappyException { protected static void throw_error(int errorCode) throws IOException {
throw new SnappyException(errorCode); throw new IOException(SnappyErrorCode.getErrorMessage(errorCode));
} }
} }

Binary file not shown.

View File

@ -121,15 +121,10 @@ public class SnappyOutputStream extends OutputStream
return; // no need to dump return; // no need to dump
// Compress and dump the buffer content // Compress and dump the buffer content
try { int compressedSize = Snappy.compress(uncompressed, 0, cursor, compressed, 0);
int compressedSize = Snappy.compress(uncompressed, 0, cursor, compressed, 0); writeInt(out, compressedSize);
writeInt(out, compressedSize); out.write(compressed, 0, compressedSize);
out.write(compressed, 0, compressedSize); cursor = 0;
cursor = 0;
}
catch (SnappyException e) {
throw new IOException(e.getMessage());
}
} }
@Override @Override

View File

@ -24,8 +24,19 @@
//-------------------------------------- //--------------------------------------
package org.xerial.snappy; package org.xerial.snappy;
import static org.junit.Assert.*;
import java.io.File;
import java.security.ProtectionDomain;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewMethod;
import org.codehaus.plexus.classworlds.ClassWorld; import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.xerial.util.log.Logger; import org.xerial.util.log.Logger;
@ -33,6 +44,7 @@ public class SnappyLoaderTest
{ {
private static Logger _logger = Logger.getLogger(SnappyLoaderTest.class); private static Logger _logger = Logger.getLogger(SnappyLoaderTest.class);
@Ignore
@Test @Test
public void loadFromSytemClassLoader() throws Exception { public void loadFromSytemClassLoader() throws Exception {
@ -41,8 +53,53 @@ public class SnappyLoaderTest
ClassRealm L1 = cw.newRealm("l1", parent); ClassRealm L1 = cw.newRealm("l1", parent);
ClassRealm L2 = cw.newRealm("l2", parent); ClassRealm L2 = cw.newRealm("l2", parent);
File nativeLib = SnappyLoader.findNativeLibrary();
assertNotNull(nativeLib);
ClassPool pool = ClassPool.getDefault();
CtClass cl = pool.makeClass("org.xerial.snappy.SnappyNativeLoader");
cl.addField(CtField.make("static boolean isLoaded = false;", cl));
String body = "public static void load(String lib) {if(!isLoaded) { try { System.load(lib); isLoaded=true; } catch(Exception e) { e.printStackTrace(); } }}";
cl.addMethod(CtNewMethod.make(body, cl));
ProtectionDomain systemPD = System.class.getProtectionDomain();
byte[] bytecode = cl.toBytecode();
// FileOutputStream f = new FileOutputStream("src/main/java/org/xerial/snappy/SnappyNativeLoader.bytecode");
// f.write(bytecode);
// f.close();
//Class< ? > loaderClass = cl.toClass(parent, System.class.getProtectionDomain());
//_logger.info(cl.getName());
//Class< ? > loaderClass = cl.toClass();
Class< ? > classLoader = Class.forName("java.lang.ClassLoader");
java.lang.reflect.Method defineClass = classLoader.getDeclaredMethod("defineClass", new Class[] { String.class,
byte[].class, int.class, int.class, ProtectionDomain.class });
defineClass.setAccessible(true);
defineClass.invoke(parent, cl.getName(), bytecode, 0, bytecode.length, System.class.getProtectionDomain());
Class< ? > forName = parent.loadClass("org.xerial.snappy.SnappyNativeLoader");
_logger.info(forName.toString());
//Class< ? > snappyClass = L1.loadClass("org.xerial.snappy.Snappy"); // not found //Class< ? > snappyClass = L1.loadClass("org.xerial.snappy.Snappy"); // not found
//_logger.info(snappyClass.getName()); //_logger.info(snappyClass.getName());
} }
@Test
public void load() throws Exception {
//SnappyLoader.load();
// Class< ? > c1 = Class.forName("org.xerial.snappy.SnappyNative");
// Class< ? > c2 = Class.forName("org.xerial.snappy.Snappy");
// ClassLoader cl1 = c1.getClassLoader();
// ClassLoader cl2 = c2.getClassLoader();
// Method m = c1.getDeclaredMethod("nativeLibraryVersion");
// m.setAccessible(true);
// String version = (String) m.invoke(null);
// _logger.info(version);
//_logger.info(SnappyNative.nativeLibraryVersion());
_logger.info(Snappy.getNativeLibraryVersion());
}
} }

View File

@ -26,6 +26,7 @@ package org.xerial.snappy;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.junit.Assert; import org.junit.Assert;
@ -294,9 +295,16 @@ public class SnappyTest
byte[] uncompressed = Snappy.uncompress(b); byte[] uncompressed = Snappy.uncompress(b);
fail("cannot reach here since the input is invalid data"); fail("cannot reach here since the input is invalid data");
} }
catch (SnappyException e) { catch (IOException e) {
assertEquals(SnappyErrorCode.FAILED_TO_UNCOMPRESS, e.errorCode); //assertEquals(SnappyErrorCode.FAILED_TO_UNCOMPRESS, e.errorCode);
} }
// catch (Exception e) {
// Class< ? > c = e.getClass();
// ClassLoader cl = c.getClassLoader();
//
// ClassLoader cl2 = SnappyException.class.getClassLoader();
// _logger.error(e);
// }
} }