org.nuiton.j2r.jni.RNative Maven / Gradle / Ivy
/*
* #%L
* Nuiton Java-2-R
* %%
* Copyright (C) 2006 - 2013 CodeLutin
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
package org.nuiton.j2r.jni;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Backport code from JNA project to load native file from classpath.
*
* See https://github.com/twall/jna/blob/master/src/com/sun/jna/Native.java
*
* @author Chatellier Eric
*/
public class RNative {
private static Log log = LogFactory.getLog(RNative.class);
public static final String RESOURCE_PREFIX;
public static final boolean IS_WINDOWS;
public static final boolean IS_LINUX;
static {
String osName = System.getProperty("os.name");
IS_WINDOWS = osName.startsWith("Windows");
IS_LINUX = osName.startsWith("Linux");
RESOURCE_PREFIX = getNativeLibraryResourcePrefix();
}
static String getNativeLibraryResourcePrefix() {
return getNativeLibraryResourcePrefix(System.getProperty("os.arch"));
}
/**
* Generate a canonical String prefix based on the given OS
* type/arch/name.
*
* @param arch from os.arch
System property
*/
static String getNativeLibraryResourcePrefix(String arch) {
String osPrefix;
arch = arch.toLowerCase().trim();
if ("x86_64".equals(arch) || "amd64".equals(arch)) {
arch = "x86-64";
} else {
arch = "x86";
}
if (IS_WINDOWS) {
osPrefix = "win32-" + arch;
} else if (IS_LINUX) {
osPrefix = "linux-" + arch;
} else {
throw new UnsatisfiedLinkError("Unsupported platform/arch");
}
return osPrefix;
}
static boolean loadNativeRLibraryFromClasspath() {
boolean result = false;
try {
String libName = "/" + RESOURCE_PREFIX + "/" + System.mapLibraryName("jri");
File lib = extractFromResourcePath(libName, RNative.class.getClassLoader());
if (lib == null) {
if (lib == null) {
throw new UnsatisfiedLinkError("Could not find R native support");
}
}
System.load(lib.getAbsolutePath());
if (log.isInfoEnabled()) {
log.info("Loaded jri native library from classpath (" + libName + ")");
}
result = true;
} catch(IOException ex) {
// can't copy lib file on local filesystem
throw new UnsatisfiedLinkError(ex.getMessage());
} catch(UnsatisfiedLinkError ex) {
// dependency libR.so (or R.dll) not found, R not installed ?
if (log.isErrorEnabled()) {
log.error("Can't load jri lib dependencies. R not found or not installed", ex);
}
}
return result;
}
/**
* Attempt to extract a native library from the resource path using the
* given class loader.
* @param name Base name of native library to extract. May also be an
* absolute resource path (i.e. starts with "/"), in which case the
* no transformations of the library name are performed. If only the base
* name is given, the resource path is attempted both with and without
* {@code Platform#RESOURCE_PREFIX}, after mapping the library name via
* {@code NativeLibrary#mapSharedLibraryName(String)}.
* @param loader Class loader to use to load resources
* @return File indicating extracted resource on disk
* @throws IOException if resource not found
*/
public static File extractFromResourcePath(String name, ClassLoader loader) throws IOException {
if (loader == null) {
loader = Thread.currentThread().getContextClassLoader();
// Context class loader is not guaranteed to be set
if (loader == null) {
loader = RNative.class.getClassLoader();
}
}
String resourcePath = name;
if (resourcePath.startsWith("/")) {
resourcePath = resourcePath.substring(1);
}
InputStream is = loader.getResourceAsStream(resourcePath);
if (is == null) {
throw new IOException("Can't obtain InputStream for " + resourcePath);
}
File lib = null;
FileOutputStream fos = null;
try {
// Suffix is required on windows, or library fails to load
// Let Java pick the suffix, except on windows, to avoid
// problems with Web Start.
lib = File.createTempFile("nuiton-j2r-", IS_WINDOWS ? ".dll" : null);
lib.deleteOnExit();
fos = new FileOutputStream(lib);
int count;
byte[] buf = new byte[1024];
while ((count = is.read(buf, 0, buf.length)) > 0) {
fos.write(buf, 0, count);
}
}
catch(IOException e) {
throw new IOException("Failed to create temporary file for " + name + " library: " + e.getMessage());
}
finally {
try { is.close(); } catch(IOException e) { }
if (fos != null) {
try { fos.close(); } catch(IOException e) { }
}
}
return lib;
}
}