package org.stagemonitor.os;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarLoader;
/**
* Extracts the native bindings for sigar and tells sigar where to find them.
* That way, that there is no need to specify the Java library path (<code>-Djava.library.path=/sigar</code>).
* <p/>
* Inspired by http://frommyplayground.com/how-to-load-native-jni-library-from-jar
*
* @author Adam Heirnich <adam@adamh.cz>, http://www.adamh.cz
*/
public final class SigarNativeBindingLoader {
/**
* This system property tells sigar where to find the native bindings
*/
private static final String SIGAR_PATH = "org.hyperic.sigar.path";
/**
* This is the directory that contains the native bindings
*/
private static final String SIGAR_RESOURCE_DIR = "/sigar/";
private SigarNativeBindingLoader() {
}
/**
* Extracts the native bindings for sigar and tells sigar where to find them.
*
* @return <code>false</code>, if sigar has already been set up, <code>true</code> otherwise
* @throws Exception
*/
public static boolean loadNativeSigarBindings() throws Exception {
final String sigarPath = System.getProperty(SIGAR_PATH);
if (sigarPath != null) {
// sigar is already set up
return false;
}
final File tempDirectory = new File(System.getProperty("java.io.tmpdir"), "sigar");
tempDirectory.mkdir();
loadNativeSigarBindings(tempDirectory);
return true;
}
/**
* Extracts the native bindings for sigar and tells sigar where to find them.
*
* @param nativeLibDir the directory the native libs should be stored to
* @return the folder that contains the extracted nativeLibs
* @throws Exception
*/
private static void loadNativeSigarBindings(File nativeLibDir) throws Exception {
extractFromJarToTemp(SIGAR_RESOURCE_DIR + new SigarLoader(Sigar.class).getLibraryName(), nativeLibDir);
System.setProperty(SIGAR_PATH, nativeLibDir.getAbsolutePath());
nativeLibDir.getAbsolutePath();
}
/**
* Loads library from current JAR archive
* <p/>
* 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 filename inside JAR as absolute path (beginning with '/'), e.g. /package/File.ext
* @param tempDirectory
* @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 extractFromJarToTemp(String path, File tempDirectory) throws IOException {
File temp = createTempFile(path, tempDirectory);
if (temp == null) {
// already extracted
return;
}
writeFromJarToTempFile(path, temp);
}
private static File createTempFile(String path, File tempDirectory) throws IOException {
if (!path.startsWith("/")) {
throw new IllegalArgumentException("The path has to be absolute (start with '/').");
}
// Obtain filename from path
String[] parts = path.split("/");
// Prepare temporary file
File temp = new File(tempDirectory, parts[parts.length - 1]);
if (temp.exists()) {
// already extracted
return null;
}
if (!temp.createNewFile()) {
throw new IOException("Could not create new File");
}
if (!temp.exists()) {
throw new FileNotFoundException("File " + temp.getAbsolutePath() + " does not exist.");
}
return temp;
}
private static void writeFromJarToTempFile(String path, File temp) throws IOException {
// Prepare buffer for data copying
byte[] buffer = new byte[1024];
int readBytes;
// Open and check input stream
InputStream is = SigarNativeBindingLoader.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();
}
}
}