/* * Class NativeUtils is published under the The MIT License: * * Copyright (c) 2012 Adam Heinrich * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package cz.adamh.utils; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.file.FileSystemNotFoundException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.ProviderNotFoundException; import java.nio.file.StandardCopyOption; /** * A simple library class which helps with loading dynamic libraries stored in * the * JAR archive. These libraries usualy contain implementation of some methods in * native code (using JNI - Java Native Interface). * * @see http://adamheinrich.com/blog/2012/how-to-load-native-jni-library-from-jar * @see https://github.com/adamheinrich/native-utils * */ public class NativeUtils { /** * The minimum length a prefix for a file has to have according to * {@link File#createTempFile(String, String)}}. */ private static final int MIN_PREFIX_LENGTH = 3; /** * Temporary directory which will contain the DLLs. */ private static File temporaryDir; /** * Private constructor - this class will never be instanced */ private NativeUtils() {} /** * Loads library from current JAR archive * * The file from JAR is copied into system temporary directory and then * loaded. The temporary file is deleted after * exiting. * Method uses String as filename because the pathname is "abstract", not * system-dependent. * * @param path * The path of file inside JAR as absolute path (beginning with * '/'), e.g. /package/File.ext * @throws IOException * If temporary file creation or read/write operation fails * @throws IllegalArgumentException * If source file (param path) does not exist * @throws IllegalArgumentException * If the path is not absolute or if the filename is shorter * than three characters * (restriction of * {@link File#createTempFile(java.lang.String, java.lang.String)}). * @throws FileNotFoundException * If the file could not be found inside the JAR. */ public static void loadLibraryFromJar(String path) throws IOException { if (!path.startsWith("/")) { throw new IllegalArgumentException("The path has to be absolute (start with '/')."); } // Obtain filename from path final String[] parts = path.split("/"); final String filename = (parts.length > 1) ? parts[parts.length - 1] : null; // Check if the filename is okay if (filename == null || filename.length() < MIN_PREFIX_LENGTH) { throw new IllegalArgumentException("The filename has to be at least 3 characters long."); } // Prepare temporary file if (temporaryDir == null) { temporaryDir = createTempDirectory("nativeutils"); temporaryDir.deleteOnExit(); } final File temp = new File(temporaryDir, filename); try (InputStream is = NativeUtils.class.getResourceAsStream(path)) { Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); } catch (final IOException e) { temp.delete(); throw e; } catch (final NullPointerException e) { temp.delete(); throw new FileNotFoundException("File " + path + " was not found inside JAR."); } try { System.load(temp.getAbsolutePath()); } finally { if (isPosixCompliant()) { // Assume POSIX compliant file system, can be deleted after loading temp.delete(); } else { // Assume non-POSIX, and don't delete until last file descriptor closed temp.deleteOnExit(); } } } private static boolean isPosixCompliant() { try { if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) { return true; } return false; } catch (FileSystemNotFoundException | ProviderNotFoundException | SecurityException e) { return false; } } private static File createTempDirectory(String prefix) throws IOException { final String tempDir = System.getProperty("java.io.tmpdir"); final File generatedDir = new File(tempDir, prefix + System.nanoTime()); if (!generatedDir.mkdir()) { throw new IOException("Failed to create temp directory " + generatedDir.getName()); } return generatedDir; } }