From d0b35b3db85124101052dac44ce7724eae87a555 Mon Sep 17 00:00:00 2001 From: Pedro Martinez Mediano Date: Thu, 1 Jun 2017 20:34:23 +1000 Subject: [PATCH] Added NativeUtils for native lib loading and included libKraskov.so in jar. --- build.xml | 2 +- ...tualInfoCalculatorMultiVariateKraskov.java | 64 +++++----- .../infodynamics/utils/NativeUtils.java | 113 ++++++++++++++++++ 3 files changed, 150 insertions(+), 29 deletions(-) create mode 100644 java/source/infodynamics/utils/NativeUtils.java diff --git a/build.xml b/build.xml index 255c923..993e26e 100755 --- a/build.xml +++ b/build.xml @@ -68,7 +68,7 @@ - + diff --git a/java/source/infodynamics/measures/continuous/kraskov/MutualInfoCalculatorMultiVariateKraskov.java b/java/source/infodynamics/measures/continuous/kraskov/MutualInfoCalculatorMultiVariateKraskov.java index c83abfe..ce03b41 100755 --- a/java/source/infodynamics/measures/continuous/kraskov/MutualInfoCalculatorMultiVariateKraskov.java +++ b/java/source/infodynamics/measures/continuous/kraskov/MutualInfoCalculatorMultiVariateKraskov.java @@ -34,6 +34,7 @@ import infodynamics.utils.MatrixUtils; import infodynamics.utils.NearestNeighbourSearcher; import infodynamics.utils.NeighbourNodeData; import infodynamics.utils.EmpiricalMeasurementDistribution; +import infodynamics.utils.NativeUtils; /** *

Computes the differential mutual information of two given multivariate sets of @@ -706,37 +707,44 @@ public abstract class MutualInfoCalculatorMultiVariateKraskov if (!cudaLibraryLoaded) { - String fullPath; + // The code below is used to load native library when it's already + // outside the jar. Now we use the NativeUtils library which can extract + // and load a library from the jar (at the expense of a small latency). + // Code is left here temporarily for future reference. + // ===================================================================== - if (gpuLibraryPath.length() < 1) { - // If user didn't provide a path, try the default - Path jarPath = Paths.get(MutualInfoCalculatorMultiVariateKraskov.class.getProtectionDomain().getCodeSource().getLocation().toURI()); - String fileSep = System.getProperty("file.separator", ""); - if (fileSep.length() == 0) { - throw new Exception("Unable to find default GPU library path. Provide path manually through setProperty()."); - } - String jarFolder = jarPath.toString().substring(0, jarPath.toString().lastIndexOf(fileSep)); - String relPath = fileSep + "bin" + fileSep + - "cuda" + fileSep + "libKraskov.so"; - fullPath = jarFolder + relPath; - } else { - // Otherwise, use the provided path. - fullPath = gpuLibraryPath; - } + // String fullPath; + // if (gpuLibraryPath.length() < 1) { + // // If user didn't provide a path, try the default + // Path jarPath = Paths.get(MutualInfoCalculatorMultiVariateKraskov.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + // String fileSep = System.getProperty("file.separator", ""); + // if (fileSep.length() == 0) { + // throw new Exception("Unable to find default GPU library path. Provide path manually through setProperty()."); + // } + // String jarFolder = jarPath.toString().substring(0, jarPath.toString().lastIndexOf(fileSep)); + // String relPath = fileSep + "bin" + fileSep + + // "cuda" + fileSep + "libKraskov.so"; + // fullPath = jarFolder + relPath; + // } else { + // // Otherwise, use the provided path. + // fullPath = gpuLibraryPath; + // } - // Check if file exists - File lib = new File(fullPath); - if (!lib.exists()) { - String errmsg = "GPU library not found. To compile GPU code set the enablegpu flag to true in build.xml"; - if (gpuLibraryPath.length() > 1) { - errmsg += "\nGPU library was not found in the path provided. Provide full path including library file name."; - errmsg += "\nExample: /home/johndoe/myfolder/libKraskov.so"; - } - throw new Exception(errmsg); - } + // // Check if file exists + // File lib = new File(fullPath); + // if (!lib.exists()) { + // String errmsg = "GPU library not found. To compile GPU code set the enablegpu flag to true in build.xml"; + // if (gpuLibraryPath.length() > 1) { + // errmsg += "\nGPU library was not found in the path provided. Provide full path including library file name."; + // errmsg += "\nExample: /home/johndoe/myfolder/libKraskov.so"; + // } + // throw new Exception(errmsg); + // } - // If it does, then try to load it - System.load(fullPath); + // // If it does, then try to load it + // System.load(fullPath); + + NativeUtils.loadLibraryFromJar("/cuda/libKraskov.so"); System.out.println("CUDA native library loaded."); cudaLibraryLoaded = true; diff --git a/java/source/infodynamics/utils/NativeUtils.java b/java/source/infodynamics/utils/NativeUtils.java new file mode 100644 index 0000000..35efd56 --- /dev/null +++ b/java/source/infodynamics/utils/NativeUtils.java @@ -0,0 +1,113 @@ +/* + * 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 infodynamics.utils; + +import java.io.*; + +/** + * 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 { + + /** + * 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 {@see File#createTempFile(java.lang.String, java.lang.String)}). + */ + 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 + String[] parts = path.split("/"); + String filename = (parts.length > 1) ? parts[parts.length - 1] : null; + + // Split filename to prexif and suffix (extension) + String prefix = ""; + String suffix = null; + if (filename != null) { + parts = filename.split("\\.", 2); + prefix = parts[0]; + suffix = (parts.length > 1) ? "."+parts[parts.length - 1] : null; // Thanks, davs! :-) + } + + // Check if the filename is okay + if (filename == null || prefix.length() < 3) { + throw new IllegalArgumentException("The filename has to be at least 3 characters long."); + } + + // Prepare temporary file + File temp = File.createTempFile(prefix, suffix); + temp.deleteOnExit(); + + if (!temp.exists()) { + throw new FileNotFoundException("File " + temp.getAbsolutePath() + " does not exist."); + } + + // Prepare buffer for data copying + byte[] buffer = new byte[1024]; + int readBytes; + + // Open and check input stream + InputStream is = NativeUtils.class.getResourceAsStream(path); + if (is == null) { + throw new FileNotFoundException("File " + path + " was not found inside JAR."); + } + + // Open output stream and copy data between source file in JAR and the temporary file + OutputStream os = new FileOutputStream(temp); + try { + while ((readBytes = is.read(buffer)) != -1) { + os.write(buffer, 0, readBytes); + } + } finally { + // If read/write fails, close streams safely before throwing an exception + os.close(); + is.close(); + } + + // Finally, load the library + System.load(temp.getAbsolutePath()); + } +}