Adjustment to NativeLibraryLoader to allow native library to be loaded

from either java.library.path or from extracting from the Jar. Means
that the test in the build do not need to rely on the Jar, useful when
creating similar builds (and executing tests) from Maven
This commit is contained in:
Adam Retter 2015-01-31 22:24:35 +00:00
parent 353db6daef
commit dd8d5471ea
4 changed files with 74 additions and 32 deletions

View File

@ -15,7 +15,9 @@ public class NativeLibraryLoader {
private static final NativeLibraryLoader instance = new NativeLibraryLoader(); private static final NativeLibraryLoader instance = new NativeLibraryLoader();
private static boolean initialized = false; private static boolean initialized = false;
private static final String sharedLibraryName = Environment.getJniLibraryName("rocksdb"); private static final String sharedLibraryName = Environment.getSharedLibraryName("rocksdb");
private static final String jniLibraryName = Environment.getJniLibraryName("rocksdb");
private static final String jniLibraryFileName = Environment.getJniLibraryFileName("rocksdb");
private static final String tempFilePrefix = "librocksdbjni"; private static final String tempFilePrefix = "librocksdbjni";
private static final String tempFileSuffix = "." + Environment.getJniLibraryExtension(); private static final String tempFileSuffix = "." + Environment.getJniLibraryExtension();
@ -28,6 +30,34 @@ public class NativeLibraryLoader {
return instance; return instance;
} }
/**
* Firstly attempts to load the library from <i>java.library.path</i>,
* if that fails then it falls back to extracting
* the library from the classpath
* {@link org.rocksdb.NativeLibraryLoader#loadLibraryFromJar(java.lang.String)}
*
* @param tmpDir A temporary directory to use
* to copy the native library to when loading from the classpath.
* If null, or the empty string, we rely on Java's
* {@link java.io.File#createTempFile(String, String)}
* function to provide a temporary location.
* The temporary file will be registered for deletion
* on exit.
*
* @throws java.io.IOException if a filesystem operation fails.
*/
public synchronized void loadLibrary(final String tmpDir) throws IOException {
try {
System.loadLibrary(sharedLibraryName);
} catch(final UnsatisfiedLinkError ule1) {
try {
System.loadLibrary(jniLibraryName);
} catch(final UnsatisfiedLinkError ule2) {
loadLibraryFromJar(tmpDir);
}
}
}
/** /**
* Attempts to extract the native RocksDB library * Attempts to extract the native RocksDB library
* from the classpath and load it * from the classpath and load it
@ -42,14 +72,14 @@ public class NativeLibraryLoader {
* *
* @throws java.io.IOException if a filesystem operation fails. * @throws java.io.IOException if a filesystem operation fails.
*/ */
public synchronized void loadLibraryFromJar(final String tmpDir) private void loadLibraryFromJar(final String tmpDir)
throws IOException { throws IOException {
if (!initialized) { if (!initialized) {
final File temp; final File temp;
if (tmpDir == null || tmpDir.equals("")) { if (tmpDir == null || tmpDir.equals("")) {
temp = File.createTempFile(tempFilePrefix, tempFileSuffix); temp = File.createTempFile(tempFilePrefix, tempFileSuffix);
} else { } else {
temp = new File(tmpDir, sharedLibraryName); temp = new File(tmpDir, jniLibraryFileName);
} }
if (!temp.exists()) { if (!temp.exists()) {
@ -60,9 +90,9 @@ public class NativeLibraryLoader {
// attempt to copy the library from the Jar file to the temp destination // attempt to copy the library from the Jar file to the temp destination
try (final InputStream is = getClass().getClassLoader(). try (final InputStream is = getClass().getClassLoader().
getResourceAsStream(sharedLibraryName)) { getResourceAsStream(jniLibraryFileName)) {
if (is == null) { if (is == null) {
throw new RuntimeException(sharedLibraryName + " was not found inside JAR."); throw new RuntimeException(jniLibraryFileName + " was not found inside JAR.");
} else { } else {
Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING);
} }

View File

@ -44,7 +44,7 @@ public class RocksDB extends RocksObject {
} }
try try
{ {
NativeLibraryLoader.getInstance().loadLibraryFromJar(tmpDir); NativeLibraryLoader.getInstance().loadLibrary(tmpDir);
} }
catch (IOException e) catch (IOException e)
{ {
@ -66,7 +66,7 @@ public class RocksDB extends RocksObject {
} }
for (String path : paths) { for (String path : paths) {
try { try {
System.load(path + "/" + Environment.getSharedLibraryName( System.load(path + "/" + Environment.getSharedLibraryFileName(
compressionType.getLibraryName())); compressionType.getLibraryName()));
break; break;
} catch (UnsatisfiedLinkError e) { } catch (UnsatisfiedLinkError e) {
@ -78,7 +78,7 @@ public class RocksDB extends RocksObject {
UnsatisfiedLinkError err = null; UnsatisfiedLinkError err = null;
for (String path : paths) { for (String path : paths) {
try { try {
System.load(path + "/" + Environment.getJniLibraryName("rocksdbjni")); System.load(path + "/" + Environment.getJniLibraryFileName("rocksdbjni"));
success = true; success = true;
break; break;
} catch (UnsatisfiedLinkError e) { } catch (UnsatisfiedLinkError e) {

View File

@ -23,20 +23,32 @@ public class Environment {
} }
public static String getSharedLibraryName(String name) { public static String getSharedLibraryName(String name) {
return name + "jni";
}
public static String getSharedLibraryFileName(String name) {
return appendLibOsSuffix("lib" + getSharedLibraryName(name), true);
}
public static String getJniLibraryName(final String name) {
if (isUnix()) { if (isUnix()) {
return String.format("lib%sjni.so", name); final String arch = (is64Bit()) ? "64" : "32";
return String.format("%sjni-linux%s", name, arch);
} else if (isMac()) { } else if (isMac()) {
return String.format("lib%sjni.dylib", name); return String.format("%sjni-osx", name);
} }
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
public static String getJniLibraryName(String name) { public static String getJniLibraryFileName(final String name) {
return appendLibOsSuffix("lib" + getJniLibraryName(name), false);
}
private static String appendLibOsSuffix(final String libraryFileName, final boolean shared) {
if (isUnix()) { if (isUnix()) {
String arch = (is64Bit()) ? "64" : "32"; return libraryFileName + ".so";
return String.format("lib%sjni-linux%s.so", name, arch);
} else if (isMac()) { } else if (isMac()) {
return String.format("lib%sjni-osx.jnilib", name); return libraryFileName + (shared ? ".dylib" : ".jnilib");
} }
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -23,9 +23,9 @@ public class EnvironmentTest {
assertThat(Environment.isWindows()).isFalse(); assertThat(Environment.isWindows()).isFalse();
assertThat(Environment.getJniLibraryExtension()). assertThat(Environment.getJniLibraryExtension()).
isEqualTo(".jnilib"); isEqualTo(".jnilib");
assertThat(Environment.getJniLibraryName("rocksdb")). assertThat(Environment.getJniLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni-osx.jnilib"); isEqualTo("librocksdbjni-osx.jnilib");
assertThat(Environment.getSharedLibraryName("rocksdb")). assertThat(Environment.getSharedLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni.dylib"); isEqualTo("librocksdbjni.dylib");
} }
@ -35,9 +35,9 @@ public class EnvironmentTest {
assertThat(Environment.isWindows()).isFalse(); assertThat(Environment.isWindows()).isFalse();
assertThat(Environment.getJniLibraryExtension()). assertThat(Environment.getJniLibraryExtension()).
isEqualTo(".jnilib"); isEqualTo(".jnilib");
assertThat(Environment.getJniLibraryName("rocksdb")). assertThat(Environment.getJniLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni-osx.jnilib"); isEqualTo("librocksdbjni-osx.jnilib");
assertThat(Environment.getSharedLibraryName("rocksdb")). assertThat(Environment.getSharedLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni.dylib"); isEqualTo("librocksdbjni.dylib");
} }
@ -48,27 +48,27 @@ public class EnvironmentTest {
assertThat(Environment.isWindows()).isFalse(); assertThat(Environment.isWindows()).isFalse();
assertThat(Environment.getJniLibraryExtension()). assertThat(Environment.getJniLibraryExtension()).
isEqualTo(".so"); isEqualTo(".so");
assertThat(Environment.getJniLibraryName("rocksdb")). assertThat(Environment.getJniLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni-linux32.so"); isEqualTo("librocksdbjni-linux32.so");
assertThat(Environment.getSharedLibraryName("rocksdb")). assertThat(Environment.getSharedLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni.so"); isEqualTo("librocksdbjni.so");
// UNIX // UNIX
setEnvironmentClassFields("Unix", "32"); setEnvironmentClassFields("Unix", "32");
assertThat(Environment.isWindows()).isFalse(); assertThat(Environment.isWindows()).isFalse();
assertThat(Environment.getJniLibraryExtension()). assertThat(Environment.getJniLibraryExtension()).
isEqualTo(".so"); isEqualTo(".so");
assertThat(Environment.getJniLibraryName("rocksdb")). assertThat(Environment.getJniLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni-linux32.so"); isEqualTo("librocksdbjni-linux32.so");
assertThat(Environment.getSharedLibraryName("rocksdb")). assertThat(Environment.getSharedLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni.so"); isEqualTo("librocksdbjni.so");
// AIX // AIX
setEnvironmentClassFields("aix", "32"); setEnvironmentClassFields("aix", "32");
assertThat(Environment.isWindows()).isFalse(); assertThat(Environment.isWindows()).isFalse();
assertThat(Environment.getJniLibraryExtension()). assertThat(Environment.getJniLibraryExtension()).
isEqualTo(".so"); isEqualTo(".so");
assertThat(Environment.getJniLibraryName("rocksdb")). assertThat(Environment.getJniLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni-linux32.so"); isEqualTo("librocksdbjni-linux32.so");
assertThat(Environment.getSharedLibraryName("rocksdb")). assertThat(Environment.getSharedLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni.so"); isEqualTo("librocksdbjni.so");
} }
@ -78,27 +78,27 @@ public class EnvironmentTest {
assertThat(Environment.isWindows()).isFalse(); assertThat(Environment.isWindows()).isFalse();
assertThat(Environment.getJniLibraryExtension()). assertThat(Environment.getJniLibraryExtension()).
isEqualTo(".so"); isEqualTo(".so");
assertThat(Environment.getJniLibraryName("rocksdb")). assertThat(Environment.getJniLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni-linux64.so"); isEqualTo("librocksdbjni-linux64.so");
assertThat(Environment.getSharedLibraryName("rocksdb")). assertThat(Environment.getSharedLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni.so"); isEqualTo("librocksdbjni.so");
// UNIX // UNIX
setEnvironmentClassFields("Unix", "x64"); setEnvironmentClassFields("Unix", "x64");
assertThat(Environment.isWindows()).isFalse(); assertThat(Environment.isWindows()).isFalse();
assertThat(Environment.getJniLibraryExtension()). assertThat(Environment.getJniLibraryExtension()).
isEqualTo(".so"); isEqualTo(".so");
assertThat(Environment.getJniLibraryName("rocksdb")). assertThat(Environment.getJniLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni-linux64.so"); isEqualTo("librocksdbjni-linux64.so");
assertThat(Environment.getSharedLibraryName("rocksdb")). assertThat(Environment.getSharedLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni.so"); isEqualTo("librocksdbjni.so");
// AIX // AIX
setEnvironmentClassFields("aix", "x64"); setEnvironmentClassFields("aix", "x64");
assertThat(Environment.isWindows()).isFalse(); assertThat(Environment.isWindows()).isFalse();
assertThat(Environment.getJniLibraryExtension()). assertThat(Environment.getJniLibraryExtension()).
isEqualTo(".so"); isEqualTo(".so");
assertThat(Environment.getJniLibraryName("rocksdb")). assertThat(Environment.getJniLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni-linux64.so"); isEqualTo("librocksdbjni-linux64.so");
assertThat(Environment.getSharedLibraryName("rocksdb")). assertThat(Environment.getSharedLibraryFileName("rocksdb")).
isEqualTo("librocksdbjni.so"); isEqualTo("librocksdbjni.so");
} }
@ -111,13 +111,13 @@ public class EnvironmentTest {
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void failWinJniLibraryName(){ public void failWinJniLibraryName(){
setEnvironmentClassFields("win", "x64"); setEnvironmentClassFields("win", "x64");
Environment.getJniLibraryName("rocksdb"); Environment.getJniLibraryFileName("rocksdb");
} }
@Test(expected = UnsupportedOperationException.class) @Test(expected = UnsupportedOperationException.class)
public void failWinSharedLibrary(){ public void failWinSharedLibrary(){
setEnvironmentClassFields("win", "x64"); setEnvironmentClassFields("win", "x64");
Environment.getSharedLibraryName("rocksdb"); Environment.getSharedLibraryFileName("rocksdb");
} }
private void setEnvironmentClassFields(String osName, private void setEnvironmentClassFields(String osName,