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