1 // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 2 package org.rocksdb; 3 4 import java.io.*; 5 import java.nio.file.Files; 6 import java.nio.file.StandardCopyOption; 7 8 import org.rocksdb.util.Environment; 9 10 /** 11 * This class is used to load the RocksDB shared library from within the jar. 12 * The shared library is extracted to a temp folder and loaded from there. 13 */ 14 public class NativeLibraryLoader { 15 //singleton 16 private static final NativeLibraryLoader instance = new NativeLibraryLoader(); 17 private static boolean initialized = false; 18 19 private static final String sharedLibraryName = Environment.getSharedLibraryName("rocksdb"); 20 private static final String jniLibraryName = Environment.getJniLibraryName("rocksdb"); 21 private static final String jniLibraryFileName = Environment.getJniLibraryFileName("rocksdb"); 22 private static final String tempFilePrefix = "librocksdbjni"; 23 private static final String tempFileSuffix = Environment.getJniLibraryExtension(); 24 25 /** 26 * Get a reference to the NativeLibraryLoader 27 * 28 * @return The NativeLibraryLoader 29 */ getInstance()30 public static NativeLibraryLoader getInstance() { 31 return instance; 32 } 33 34 /** 35 * Firstly attempts to load the library from <i>java.library.path</i>, 36 * if that fails then it falls back to extracting 37 * the library from the classpath 38 * {@link org.rocksdb.NativeLibraryLoader#loadLibraryFromJar(java.lang.String)} 39 * 40 * @param tmpDir A temporary directory to use 41 * to copy the native library to when loading from the classpath. 42 * If null, or the empty string, we rely on Java's 43 * {@link java.io.File#createTempFile(String, String)} 44 * function to provide a temporary location. 45 * The temporary file will be registered for deletion 46 * on exit. 47 * 48 * @throws java.io.IOException if a filesystem operation fails. 49 */ loadLibrary(final String tmpDir)50 public synchronized void loadLibrary(final String tmpDir) throws IOException { 51 try { 52 System.loadLibrary(sharedLibraryName); 53 } catch(final UnsatisfiedLinkError ule1) { 54 try { 55 System.loadLibrary(jniLibraryName); 56 } catch(final UnsatisfiedLinkError ule2) { 57 loadLibraryFromJar(tmpDir); 58 } 59 } 60 } 61 62 /** 63 * Attempts to extract the native RocksDB library 64 * from the classpath and load it 65 * 66 * @param tmpDir A temporary directory to use 67 * to copy the native library to. If null, 68 * or the empty string, we rely on Java's 69 * {@link java.io.File#createTempFile(String, String)} 70 * function to provide a temporary location. 71 * The temporary file will be registered for deletion 72 * on exit. 73 * 74 * @throws java.io.IOException if a filesystem operation fails. 75 */ loadLibraryFromJar(final String tmpDir)76 void loadLibraryFromJar(final String tmpDir) 77 throws IOException { 78 if (!initialized) { 79 System.load(loadLibraryFromJarToTemp(tmpDir).getAbsolutePath()); 80 initialized = true; 81 } 82 } 83 loadLibraryFromJarToTemp(final String tmpDir)84 File loadLibraryFromJarToTemp(final String tmpDir) 85 throws IOException { 86 final File temp; 87 if (tmpDir == null || tmpDir.isEmpty()) { 88 temp = File.createTempFile(tempFilePrefix, tempFileSuffix); 89 } else { 90 temp = new File(tmpDir, jniLibraryFileName); 91 if (temp.exists() && !temp.delete()) { 92 throw new RuntimeException("File: " + temp.getAbsolutePath() 93 + " already exists and cannot be removed."); 94 } 95 if (!temp.createNewFile()) { 96 throw new RuntimeException("File: " + temp.getAbsolutePath() 97 + " could not be created."); 98 } 99 } 100 101 if (!temp.exists()) { 102 throw new RuntimeException("File " + temp.getAbsolutePath() + " does not exist."); 103 } else { 104 temp.deleteOnExit(); 105 } 106 107 // attempt to copy the library from the Jar file to the temp destination 108 try (final InputStream is = getClass().getClassLoader(). 109 getResourceAsStream(jniLibraryFileName)) { 110 if (is == null) { 111 throw new RuntimeException(jniLibraryFileName + " was not found inside JAR."); 112 } else { 113 Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); 114 } 115 } 116 117 return temp; 118 } 119 120 /** 121 * Private constructor to disallow instantiation 122 */ NativeLibraryLoader()123 private NativeLibraryLoader() { 124 } 125 } 126