27
27
import java .io .FileOutputStream ;
28
28
import java .io .IOException ;
29
29
import java .io .InputStream ;
30
+ import java .nio .file .Files ;
31
+ import java .nio .file .Path ;
32
+ import java .nio .file .attribute .PosixFileAttributeView ;
33
+ import java .nio .file .attribute .PosixFilePermission ;
34
+ import java .util .Comparator ;
30
35
import java .util .Locale ;
31
36
import java .util .Properties ;
32
- import java .util .UUID ;
37
+ import java .util .Set ;
38
+ import java .util .stream .Stream ;
33
39
34
40
/** Helper class that finds native libraries embedded as resources and loads them dynamically. */
35
41
public class NativeLibLoader {
@@ -39,6 +45,26 @@ public class NativeLibLoader {
39
45
/** Path (relative to jar) where native libraries are located. */
40
46
private static final String LIB_RESOURCE_DIR = "/lib" ;
41
47
48
+ /** Temporary directory where native libraries will be extracted. */
49
+ private static Path tempDir ;
50
+
51
+ static {
52
+ try {
53
+ tempDir = Files .createTempDirectory ("tileDbNativeLibLoader" );
54
+ } catch (IOException e ) {
55
+ e .printStackTrace (System .err );
56
+ }
57
+ Runtime .getRuntime ().addShutdownHook (new Thread (() -> {
58
+ try (Stream <Path > walk = Files .walk (tempDir )) {
59
+ walk .sorted (Comparator .reverseOrder ())
60
+ .map (Path ::toFile )
61
+ .forEach (File ::delete );
62
+ } catch (IOException e ) {
63
+ e .printStackTrace (System .err );
64
+ }
65
+ }));
66
+ }
67
+
42
68
/** Finds and loads native TileDB. */
43
69
static void loadNativeTileDB () {
44
70
try {
@@ -218,56 +244,39 @@ private static String getOSClassifier() {
218
244
*
219
245
* @param libraryDir Path of directory containing native library
220
246
* @param libraryName Name of native library
221
- * @param targetDir Path of target directory to extract library to
222
247
* @param mapLibraryName If true, transform libraryName with System.mapLibraryName
223
248
* @return File pointing to the extracted library
224
249
*/
225
- private static File extractLibraryFile (
226
- String libraryDir , String libraryName , String targetDir , boolean mapLibraryName ) {
250
+ private static Path extractLibraryFile (
251
+ String libraryDir , String libraryName , boolean mapLibraryName ) {
227
252
String libraryFileName = mapLibraryName ? System .mapLibraryName (libraryName ) : libraryName ;
228
253
String nativeLibraryFilePath = libraryDir + "/" + libraryFileName ;
229
-
230
- // Attach UUID to the native library file to ensure multiple class loaders can read the
231
- // native lib multiple times.
232
- String uuid = UUID .randomUUID ().toString ();
233
- String extractedLibFileName = String .format ("%s-%s-%s" , libraryName , uuid , libraryFileName );
234
- File extractedLibFile = new File (targetDir , extractedLibFileName );
254
+ Path extractedLibFile = tempDir .resolve (libraryFileName );
235
255
236
256
try {
237
257
// Extract a native library file into the target directory
238
- InputStream reader = null ;
239
- FileOutputStream writer = null ;
240
- try {
241
- reader = NativeLibLoader .class .getResourceAsStream (nativeLibraryFilePath );
242
- try {
243
- writer = new FileOutputStream (extractedLibFile );
258
+ try (InputStream reader = NativeLibLoader .class .getResourceAsStream (nativeLibraryFilePath );
259
+ FileOutputStream writer = new FileOutputStream (extractedLibFile .toFile ())) {
244
260
245
- byte [] buffer = new byte [8192 ];
246
- int bytesRead = 0 ;
247
- while ((bytesRead = reader .read (buffer )) != -1 ) {
248
- writer .write (buffer , 0 , bytesRead );
249
- }
250
- } finally {
251
- if (writer != null ) {
252
- writer .close ();
253
- }
254
- }
255
- } finally {
256
- if (reader != null ) {
257
- reader .close ();
261
+ byte [] buffer = new byte [8192 ];
262
+ int bytesRead = 0 ;
263
+ while ((bytesRead = reader .read (buffer )) != -1 ) {
264
+ writer .write (buffer , 0 , bytesRead );
258
265
}
259
-
260
- // Delete the extracted lib file on JVM exit.
261
- extractedLibFile .deleteOnExit ();
262
266
}
263
267
264
- // Set executable (x) flag to enable Java to load the native library
265
- boolean success =
266
- extractedLibFile .setReadable (true )
267
- && extractedLibFile .setWritable (true , true )
268
- && extractedLibFile .setExecutable (true );
269
- if (!success ) {
270
- // Setting file flag may fail, but in this case another error will be thrown in later phase
268
+ // Set executable (x) flag to enable Java to load the native library on
269
+ // UNIX platforms
270
+ PosixFileAttributeView view = Files .getFileAttributeView (
271
+ extractedLibFile , PosixFileAttributeView .class );
272
+ if (view != null ) {
273
+ // On a UNIX platform
274
+ Set <PosixFilePermission > permissions =
275
+ view .readAttributes ().permissions ();
276
+ permissions .add (PosixFilePermission .OWNER_READ );
277
+ permissions .add (PosixFilePermission .OWNER_WRITE );
278
+ permissions .add (PosixFilePermission .OWNER_EXECUTE );
279
+ view .setPermissions (permissions );
271
280
}
272
281
273
282
// Check whether the contents are properly copied from the resource folder
@@ -276,7 +285,7 @@ private static File extractLibraryFile(
276
285
InputStream extractedLibIn = null ;
277
286
try {
278
287
nativeIn = NativeLibLoader .class .getResourceAsStream (nativeLibraryFilePath );
279
- extractedLibIn = new FileInputStream (extractedLibFile );
288
+ extractedLibIn = new FileInputStream (extractedLibFile . toFile () );
280
289
281
290
if (!contentsEquals (nativeIn , extractedLibIn )) {
282
291
throw new IOException (
@@ -292,7 +301,7 @@ private static File extractLibraryFile(
292
301
}
293
302
}
294
303
295
- return new File ( targetDir , extractedLibFileName ) ;
304
+ return extractedLibFile ;
296
305
} catch (IOException e ) {
297
306
e .printStackTrace (System .err );
298
307
return null ;
@@ -306,7 +315,7 @@ private static File extractLibraryFile(
306
315
* @param mapLibraryName If true, transform libraryName with System.mapLibraryName
307
316
* @return File pointing to the extracted library
308
317
*/
309
- private static File findNativeLibrary (String libraryName , boolean mapLibraryName ) {
318
+ private static Path findNativeLibrary (String libraryName , boolean mapLibraryName ) {
310
319
String mappedLibraryName = mapLibraryName ? System .mapLibraryName (libraryName ) : libraryName ;
311
320
String libDir = LIB_RESOURCE_DIR + "/" + getOSClassifier ();
312
321
String libPath = libDir + "/" + mappedLibraryName ;
@@ -316,17 +325,8 @@ private static File findNativeLibrary(String libraryName, boolean mapLibraryName
316
325
return null ;
317
326
}
318
327
319
- // Temporary folder for the extracted native lib.
320
- File tempFolder = new File (System .getProperty ("java.io.tmpdir" ));
321
- if (!tempFolder .exists ()) {
322
- boolean created = tempFolder .mkdirs ();
323
- if (!created ) {
324
- // if created == false, it will fail eventually in the later part
325
- }
326
- }
327
-
328
328
// Extract and load a native library inside the jar file
329
- return extractLibraryFile (libDir , libraryName , tempFolder . getAbsolutePath (), mapLibraryName );
329
+ return extractLibraryFile (libDir , libraryName , mapLibraryName );
330
330
}
331
331
332
332
/**
@@ -336,10 +336,10 @@ private static File findNativeLibrary(String libraryName, boolean mapLibraryName
336
336
* @param mapLibraryName If true, transform libraryName with System.mapLibraryName
337
337
*/
338
338
private static void loadNativeLib (String libraryName , boolean mapLibraryName ) {
339
- File nativeLibFile = findNativeLibrary (libraryName , mapLibraryName );
339
+ Path nativeLibFile = findNativeLibrary (libraryName , mapLibraryName );
340
340
if (nativeLibFile != null ) {
341
341
// Load extracted or specified native library.
342
- System .load (nativeLibFile .getAbsolutePath ());
342
+ System .load (nativeLibFile .toString ());
343
343
} else {
344
344
// Try loading preinstalled library (in the path -Djava.library.path)
345
345
System .loadLibrary (libraryName );
0 commit comments