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());
+ }
+}