r295 - in trunk/src/main: java/org/nuiton/j2r/jni resources
Author: echatellier Date: 2013-10-31 23:43:33 +0100 (Thu, 31 Oct 2013) New Revision: 295 Url: http://nuiton.org/projects/nuiton-j2r/repository/revisions/295 Log: fixes #2883: Autoload nativelibs depending on java platform and arch Added: trunk/src/main/java/org/nuiton/j2r/jni/RNative.java trunk/src/main/resources/linux-x86-64/ trunk/src/main/resources/linux-x86/ trunk/src/main/resources/win32-x86-64/ Removed: trunk/src/main/resources/linux-amd64/ trunk/src/main/resources/linux-i386/ trunk/src/main/resources/win32-amd64/ Modified: trunk/src/main/java/org/nuiton/j2r/jni/RJniEngine.java Modified: trunk/src/main/java/org/nuiton/j2r/jni/RJniEngine.java =================================================================== --- trunk/src/main/java/org/nuiton/j2r/jni/RJniEngine.java 2013-10-31 21:06:25 UTC (rev 294) +++ trunk/src/main/java/org/nuiton/j2r/jni/RJniEngine.java 2013-10-31 22:43:33 UTC (rev 295) @@ -5,7 +5,7 @@ * $Id$ * $HeadURL$ * %% - * Copyright (C) 2006 - 2012 CodeLutin, Chatellier Eric + * Copyright (C) 2006 - 2013 CodeLutin, Chatellier Eric * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as @@ -50,7 +50,7 @@ */ public class RJniEngine extends REngineAbstract implements REngine { - private Log log = LogFactory.getLog(RJniEngine.class); + private static Log log = LogFactory.getLog(RJniEngine.class); /** * Rengine is made to be static */ @@ -84,11 +84,13 @@ //jriLoaded is false is rJava did not find jri library if (!Rengine.jriLoaded) { - if (log.isErrorEnabled()) { - log.error( - "Cannot find jri library, make sure it is correctly installed"); + if (!RNative.loadNativeRLibraryFromClasspath()) { + if (log.isErrorEnabled()) { + log.error( + "Cannot find jri library, make sure it is correctly installed"); + } + return false; } - return false; } engine = new Rengine(args, false, null); Added: trunk/src/main/java/org/nuiton/j2r/jni/RNative.java =================================================================== --- trunk/src/main/java/org/nuiton/j2r/jni/RNative.java (rev 0) +++ trunk/src/main/java/org/nuiton/j2r/jni/RNative.java 2013-10-31 22:43:33 UTC (rev 295) @@ -0,0 +1,170 @@ +/* + * #%L + * Nuiton Java-2-R library + * + * $Id: RJniEngine.java 271 2012-02-09 10:27:30Z echatellier $ + * $HeadURL: http://svn.nuiton.org/svn/nuiton-j2r/trunk/src/main/java/org/nuiton/j2r/jni/... $ + * %% + * Copyright (C) 2006 - 2013 CodeLutin, Chatellier Eric + * %% + * 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 + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #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 <code>os.arch</code> System property + * @param name from <code>os.name</code> System property + */ + static String getNativeLibraryResourcePrefix(String arch) { + String osPrefix; + arch = arch.toLowerCase().trim(); + if ("i386".equals(arch)) { + arch = "x86"; + } + else if ("x86_64".equals(arch) || "amd64".equals(arch)) { + arch = "x86-64"; + } + + if (IS_WINDOWS) { + osPrefix = "win32-" + arch; + } else if (IS_LINUX) { + osPrefix = "linux-" + arch; + } else { + throw new UnsatisfiedLinkError("Unsupported plaform/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()); + result = true; + if (log.isInfoEnabled()) { + log.info("Loaded jri native library from classpath (" + libName + ")"); + } + } catch(IOException e) { + throw new UnsatisfiedLinkError(e.getMessage()); + } + 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 + * {@link Platform#RESOURCE_PREFIX}, after mapping the library name via + * {@link 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; + } +}
participants (1)
-
echatellier@users.nuiton.org