/** * LibLoader * * Projet created for loading NativeFmod & NativeFmodEx libraries. * Copyright � 2007-2010 J�r�me JOUVIE (Jouvieje) * * Created on 25 mar. 2007 * @version file v1.2 * @author J�r�me JOUVIE (Jouvieje) * @site http://jerome.jouvie.free.fr/ * @mail jerome.jouvie@gmail.com * * * INTRODUCTION * Project created to fix library loading, System.loadLibrary load library * with RTLD_LOCAL, RTLD_GLOCAL is needed for loading well NativeFmodEx under linux. * * * GNU LESSER GENERAL PUBLIC LICENSE * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of the License, * or (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the * Free Software Foundation, Inc., * 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA */ package org.jouvieje.libloader; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Iterator; import java.util.StringTokenizer; import java.util.Vector; import org.jdesktop.applet.util.JNLPAppletLauncher; import org.jdesktop.applet.util.JNLPAppletLauncher.LibraryLoader; /** * Utility to load library<BR> * <ul> * <li>System.load</li> * <li>System.loadLibrary</li> * <li>JNLPAppletLauncher</li> * <li>LWJGL applet launcher</li> * </ul> */ public class LibLoader { /** Windows platform */ public final static int PLATFORM_WINDOWS = 1; /** Linux platform */ public final static int PLATFORM_LINUX = 2; /** Mac platform */ public final static int PLATFORM_MACOSX = 3; /** SOLARIS platform */ public final static int PLATFORM_SOLARIS = 4; private final static String NOT_RECOGNIZED = "Unrecognized platform"; private final static String x64 = "64"; private final static String lib = "lib"; private final static String dll = ".dll"; private final static String so = ".so"; private final static String jnilib = ".jnilib"; /** Prints debug output in System.out.<BR> * Can be forced with JVM paramter <code>-Dorg.jouvieje.libloader.debug=true</code> */ public static boolean DEBUG = false; /** Are libraries loaded ? */ private static boolean librariesLoaded = false; /** Platform */ private static int platform = -1; /** Platform bitness */ private static boolean platform64 = false; /* Internal Utilities */ /** @return true if LibLoader library is loaded * @see # */ protected static boolean isLibLoaderLibsLoaded() { if(!librariesLoaded) { //List of libraries- Only linux final String[][] libs = new String[][] { {"LibLoader64", "libLibLoader64.so"}, {"LibLoader", "libLibLoader.so",} }; //Load until one library has been loaded for(int i = 0; i < libs.length; i++) { final String[] libNames = libs[i]; if(loadWithSystem(libNames[0], libNames[1])) { librariesLoaded = true; break; } } } return librariesLoaded; } /** * @see #PLATFORM_WINDOWS * @see #PLATFORM_LINUX * @see #PLATFORM_MACOSX * @return the current platform type */ public static int getPlatform() { if(platform == -1) { final String osName = getProperty("os.name").toLowerCase(); final String osArch = getProperty("os.arch"); printlnDebug("os.name: "+osName); printlnDebug("os.arch: "+osArch); boolean bits64 = (osArch.indexOf("64") != -1); if (osName.startsWith("win")) { platform = PLATFORM_WINDOWS; } else if (osName.startsWith("linux") || osName.startsWith("freebsd") || osName.startsWith("sunos")) { platform = PLATFORM_LINUX; } else if (osName.startsWith("mac")) { platform = PLATFORM_MACOSX; } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) { platform = PLATFORM_SOLARIS; if(osArch.indexOf("sparcv9") != -1) { bits64 = true; } } else { throw new RuntimeException(NOT_RECOGNIZED); } platform64 = bits64; } return platform; } public static boolean isPlatform64Bits() { getPlatform(); //Initialize platform64 return platform64; } public static boolean isWindowsCE() { final String osName = getProperty("os.name").toLowerCase(); return (osName.indexOf("ce") != -1); } /* Utility to access system properties */ private static String getProperty(final String prop) { return (String)AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return System.getProperty(prop); }}); } private static boolean exists(final File file) { return ((Boolean)AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return new Boolean(file.exists()); }})).booleanValue(); } /* Loading Interface */ /** @return true if the library has been loaded */ public static boolean loadLibrary(String libraryName, boolean libLoaderFirst) { return loadLibrary(defaultPlatformSpecificConfig(libraryName), libLoaderFirst); } /** @return true if the library has been loaded */ private static LibraryConfig defaultPlatformSpecificConfig(String libraryName) { final LibraryConfig libConfig = new LibraryConfig(); libConfig.windowsLibraries = new PlatformLibrary(libraryName, libraryName+dll); libConfig.windows64Libraries = new PlatformLibrary(libraryName+x64, libraryName+x64+dll); libConfig.linuxLibraries = new PlatformLibrary(libraryName, lib+libraryName+so); libConfig.linux64Libraries = new PlatformLibrary(libraryName+x64, lib+libraryName+x64+so); libConfig.macLibraries = new PlatformLibrary(libraryName, lib+libraryName+jnilib); return libConfig; } /** @return true if the library has been loaded */ public static boolean loadLibrary(LibraryConfig libConfig, boolean libLoaderFirst) { switch(getPlatform()) { case PLATFORM_WINDOWS: if(loadLibrary(libConfig.windows64Libraries, libLoaderFirst)) { return true; } if(loadLibrary(libConfig.windowsLibraries, libLoaderFirst)) { return true; } if(loadLibrary(libConfig.windowsCeLibraries, libLoaderFirst)) { return true; } break; case PLATFORM_LINUX: if(loadLibrary(libConfig.linux64Libraries, libLoaderFirst)) { return true; } if(loadLibrary(libConfig.linuxLibraries, libLoaderFirst)) { return true; } break; case PLATFORM_MACOSX: if(loadLibrary(libConfig.macLibraries, libLoaderFirst)) { return true; } break; } return false; } /* Loading Implementation */ /** @return true if the library has been loaded */ private static boolean loadLibrary(PlatformLibrary library, boolean libLoaderFirst) { return (library != null) && loadLibrary(library.libraryName, library.libraryFullName, libLoaderFirst); } /** @return true if the library has been loaded */ public static boolean loadLibrary(final String libraryName, final String libraryFullName, boolean libLoaderFirst) { checkDebug(); if(libLoaderFirst) { printlnDebug("loadWithLibLoader"); if(loadWithLibLoader(libraryName, libraryFullName)) { return true; } printlnDebug("loadWithSystem"); if(loadWithSystem(libraryName, libraryFullName)) { return true; } } else { printlnDebug("loadWithSystem"); if(loadWithSystem(libraryName, libraryFullName)) { return true; } printlnDebug("loadWithLibLoader"); if(loadWithLibLoader(libraryName, libraryFullName)) { return true; } } return false; } /* Internal: Load library with System */ private static boolean loadWithSystem(final String libraryName, final String fullLibraryName) { //Find library pathes final Vector libraryPaths = new Vector(); findLibraryPathes(libraryPaths, fullLibraryName); extractLibrary(libraryPaths, fullLibraryName); findSystemPathes(libraryPaths); //Load with System.load for(Iterator i = libraryPaths.iterator(); i.hasNext(); ) { final String path = (String)i.next(); final File file = new File(path, fullLibraryName); if(!exists(file)) { printlnDebug("NOT_FOUND=" + path + " / " + fullLibraryName); continue; } printlnDebug("TRY=" + file.getAbsolutePath()); final Boolean res = (Boolean)AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { System.load(file.getAbsolutePath()); printlnDebug("LOADED=" + fullLibraryName + " | FROM=" + path); return Boolean.TRUE; } catch(Throwable t) { printlnDebug("ERROR="+t.getMessage()); return Boolean.FALSE; } }}); if(res == Boolean.TRUE) { return true; } } //Load with System.loadLibrary Boolean res = (Boolean)AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { printlnDebug("TRY=" + libraryName); System.loadLibrary(libraryName); printlnDebug("LOADED=" + libraryName); return Boolean.TRUE; } catch(Throwable t) { printlnDebug("ERROR="+t.getMessage()); return Boolean.FALSE; } }}); if(res == Boolean.TRUE) { return true; } //Use applet-launcher final boolean useAppletLauncher = Boolean.valueOf(getProperty("sun.jnlp.applet.launcher")).booleanValue(); if(useAppletLauncher) { res = (Boolean)AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { JNLPAppletLauncher.DEBUG = DEBUG; JNLPAppletLauncher.VERBOSE = DEBUG; JNLPAppletLauncher.loadLibrary(libraryName, null); printlnDebug("LOADED=" + libraryName + " FROM applet launcher"); return Boolean.TRUE; } catch(Throwable t) { printlnDebug("ERROR=" + t.getMessage()); stackTraceDebug(t); return Boolean.FALSE; } }}); if(res == Boolean.TRUE) { return true; } } return false; } /** Internal: Load library with LibLoader * Loads a library with <code>RTLD_NOW | RTLD_GLOBAL</code> mode<BR> * <I>(<code>RTLD_NOW | RTLD_GLOBAL</code> mean external symbol will be available for other libraries)</I>. * @param lib library <B><U>FULL</U></B> file name or absolute/relative path (including <B><U>FULL</U></B> library name) */ private static boolean loadWithLibLoader(final String libraryName, final String fullLibraryName) throws UnsatisfiedLinkError { if(!isLibLoaderLibsLoaded()) { return false; } //Find library pathes final Vector libraryPaths = new Vector(); findLibraryPathes(libraryPaths, fullLibraryName); extractLibrary(libraryPaths, fullLibraryName); findSystemPathes(libraryPaths); //Load from known search path for(Iterator i = libraryPaths.iterator(); i.hasNext(); ) { final String path = (String)i.next(); final File file = new File(path, fullLibraryName); if(!exists(file)) { printlnDebug("NOT_FOUND: " + path + " / " + fullLibraryName); continue; } final int mode = RTLD.RTLD_NOW | RTLD.RTLD_GLOBAL; printlnDebug("TRY: " + file.getAbsolutePath()); final long handle = LibLoaderJNI.dlopen(file.getAbsolutePath().getBytes(), mode); if(handle == 0) { printlnDebug("ERROR: " + LibLoaderJNI.dlerror()); } else { printlnDebug(fullLibraryName + " loaded from " + path + " [handle=0x" + Long.toHexString(handle) + "]"); LibLoaderJNI.dlerror(); /* Clear any error */ return true; } } //Use applet-launcher boolean useAppletLauncher = Boolean.valueOf(getProperty("sun.jnlp.applet.launcher")).booleanValue(); if(useAppletLauncher) { JNLPAppletLauncher.DEBUG = DEBUG; JNLPAppletLauncher.VERBOSE = DEBUG; final Boolean res = (Boolean)AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { JNLPAppletLauncher.loadLibrary(libraryName, new LibraryLoader() { public void loadLibrary(String fullLibraryName) throws UnsatisfiedLinkError { long handle = 0; try { printlnDebug(" DlOpen: " + fullLibraryName ); handle = openLibrary(fullLibraryName); } catch(Throwable t) { handle = 0; printlnDebug(" ERROR: " + LibLoaderJNI.dlerror()); stackTraceDebug(t); } if(handle == 0) { throw new UnsatisfiedLinkError(); } } }); return Boolean.TRUE; } catch(Throwable t) { stackTraceDebug(t); return Boolean.FALSE; } }}); return res.booleanValue(); } return false; } /** @return the handle of the library opened * @throws UnsatisfiedLinkError if failed. */ private static long openLibrary(String fullPath) { if(!isLibLoaderLibsLoaded()) { return 0; } // Loading mode final int mode = RTLD.RTLD_NOW | RTLD.RTLD_GLOBAL; // Actually load the library final long handle = LibLoaderJNI.dlopen(fullPath.getBytes(), mode); if(handle == 0) { printlnDebug("ERROR: "+LibLoaderJNI.dlerror()); } else { printlnDebug("Library successfully loaded from "+fullPath+" [handle=0x"+Long.toHexString(handle)+"]"); } LibLoaderJNI.dlerror(); /* Clear any error */ return handle; } /** @param handle handle of the librayr to close * @throws UnsatisfiedLinkError if failed. */ private static void closeLibrary(long handle) { if(!isLibLoaderLibsLoaded() || handle == 0) { return; } final int error = LibLoaderJNI.dlclose(handle); if(error != 0) { printlnDebug("Fail to close library."); String s = LibLoaderJNI.dlerror(); printlnDebug("ERROR: " + s); throw new UnsatisfiedLinkError(s); } else { printlnDebug("Library sucessfully closed."); } } /** @param libraryPathes destination for possible system pathes for the specified library */ private static void findSystemPathes(Vector libraryPathes) { // From java.library.path addSystemPathes(libraryPathes, getProperty("java.library.path")); // From org.lwjgl.librarypath addSystemPathes(libraryPathes, getProperty("org.lwjgl.librarypath")); } private static void addSystemPathes(Vector libraryPathes, String pathes) { if(pathes != null) { final StringTokenizer tokens = new StringTokenizer(pathes, File.pathSeparator); while(tokens.hasMoreTokens()) { libraryPathes.add(tokens.nextToken()); } } } /** @param libraryPathes destination for possible pathes for the specified library */ private static void findLibraryPathes(Vector libraryPathes, String fullName) { //Full path ? final File file = new File(fullName); if(file.isAbsolute()) { final File folder; if(!file.isDirectory()) { folder = file.getParentFile(); } else { folder = file; } libraryPathes.add(folder.getAbsoluteFile()); } } private static void extractLibrary(Vector libraryPathes, String fullName) { //FIXME Do in the last case //Extract library final String extractionDir = extractLibraryFromClasspath(fullName); if(extractionDir != null) { libraryPathes.add(extractionDir); } } /** @return the folder where the library has been extracted. Null if nothing has been extracted. */ private static String extractLibraryFromClasspath(final String fullName) { return (String)AccessController.doPrivileged(new PrivilegedAction() { public Object run() { final String OPEN_FOR_READING = "Opening for reading: "; try { final String pathInJar = "/"+fullName; printlnDebug(OPEN_FOR_READING+pathInJar); InputStream is = new LibLoader().getClass().getResourceAsStream(pathInJar); if(is == null) { printlnDebug("Re-"+OPEN_FOR_READING+pathInJar); is = LibLoader.class.getResourceAsStream(pathInJar); if(is == null) { printlnDebug("Re-Re-"+OPEN_FOR_READING+pathInJar); is = Class.class.getResourceAsStream(pathInJar); } } if(is != null) { printlnDebug("Open succeed."); //Create output directory String destDir = getProperty("java.io.tmpdir") + File.separator + "LibLoader" + File.separator; //TODO Add version File dir = new File(destDir); printlnDebug("Creating destination : "+destDir); dir.mkdirs(); File fileOut = new File(dir, fullName); try { //Write library to output directory BufferedInputStream in = new BufferedInputStream(is); printlnDebug("Opening output"); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileOut)); printlnDebug("Writting to output"); byte[] readBuffer = new byte[8 * 1024]; int bytesRead = 0; while((bytesRead = is.read(readBuffer)) > 0) { out.write(readBuffer, 0, bytesRead); } printlnDebug("Closing files"); in.close(); out.close(); printlnDebug("Extraction succeed"); return destDir; } catch(Throwable t) { stackTraceDebug(t); if(exists(fileOut)) { return destDir; } } } } catch(Throwable t) { stackTraceDebug(t); } return null; }}); } /* Debug */ private static void checkDebug() { //Force debug if(!DEBUG) { final String property = getProperty("org.jouvieje.libloader.debug"); if(property != null) { final boolean forceDebug = Boolean.valueOf(property).booleanValue(); if(forceDebug) { DEBUG = true; } } } } private static void printlnDebug(String s) { if(DEBUG) { java.lang.System.out.println(s); } } private static void stackTraceDebug(Throwable t) { if(DEBUG) { t.printStackTrace(); } } }