001// --- BEGIN LICENSE BLOCK --- 002/* 003 * Copyright (c) 2009, Mikio L. Braun 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or without 007 * modification, are permitted provided that the following conditions are 008 * met: 009 * 010 * * Redistributions of source code must retain the above copyright 011 * notice, this list of conditions and the following disclaimer. 012 * 013 * * Redistributions in binary form must reproduce the above 014 * copyright notice, this list of conditions and the following 015 * disclaimer in the documentation and/or other materials provided 016 * with the distribution. 017 * 018 * * Neither the name of the Technische Universit?t Berlin nor the 019 * names of its contributors may be used to endorse or promote 020 * products derived from this software without specific prior 021 * written permission. 022 * 023 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 024 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 025 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 026 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 027 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 028 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 029 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 030 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 031 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 032 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 033 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 034 */ 035// --- END LICENSE BLOCK --- 036package org.jblas.util; 037 038import org.jblas.exceptions.UnsupportedArchitectureException; 039 040import java.io.*; 041 042/** 043 * Class which allows to load a dynamic file as resource (for example, from a 044 * jar-file) 045 */ 046public class LibraryLoader { 047 048 private Logger logger; 049 private String libpath; 050 051 public LibraryLoader() { 052 logger = Logger.getLogger(); 053 libpath = null; 054 } 055 056 /** 057 * <p>Find the library <tt>libname</tt> as a resource, copy it to a tempfile 058 * and load it using System.load(). The name of the library has to be the 059 * base name, it is mapped to the corresponding system name using 060 * System.mapLibraryName(). For example, the library "foo" is called "libfoo.so" 061 * under Linux and "foo.dll" under Windows, but you just have to pass "foo" 062 * the loadLibrary().</p> 063 * <p/> 064 * <p>I'm not quite sure if this doesn't open all kinds of security holes. Any ideas?</p> 065 * <p/> 066 * <p>This function reports some more information to the "org.jblas" logger at 067 * the FINE level.</p> 068 * 069 * @param libname basename of the library 070 * @throws UnsatisfiedLinkError if library cannot be founds 071 */ 072 public void loadLibrary(String libname, boolean withFlavor, boolean noPrefix) { 073 // preload flavor libraries 074 String flavor = null; 075 if (withFlavor) { 076 logger.debug("Preloading ArchFlavor library."); 077 flavor = ArchFlavor.archFlavor(); 078 if (flavor != null && flavor.equals("sse2")) { 079 throw new UnsupportedArchitectureException("Support for SSE2 processors stopped with version 1.2.2. Sorry."); 080 } 081 } 082 logger.debug("Found flavor = '" + flavor + "'"); 083 084 libname = System.mapLibraryName(libname); 085 086 /* 087 * JDK 7 changed the ending for Mac OS from "jnilib" to "dylib". 088 * 089 * If that is the case, remap the filename. 090 */ 091 String loadLibname = libname; 092 if (libname.endsWith("dylib")) { 093 loadLibname = libname.replace(".dylib", ".jnilib"); 094 logger.config("Replaced .dylib with .jnilib"); 095 } 096 097 logger.debug("Attempting to load \"" + loadLibname + "\"."); 098 099 String[] paths = { 100 "/", 101 "/bin/", 102 fatJarLibraryPath("static", flavor), 103 fatJarLibraryPathNonUnified("static", flavor), 104 fatJarLibraryPath("dynamic", flavor), 105 fatJarLibraryPathNonUnified("dynamic", flavor), 106 }; 107 108 InputStream is = findLibrary(paths, loadLibname); 109 110 // Oh man, have to get out of here! 111 if (is == null) { 112 throw new UnsatisfiedLinkError("Couldn't find the resource " + loadLibname + "."); 113 } 114 115 logger.config("Loading " + loadLibname + " from " + libpath + ", copying to " + libname + "."); 116 loadLibraryFromStream(libname, is, noPrefix); 117 } 118 119 private InputStream findLibrary(String[] paths, String libname) { 120 InputStream is = null; 121 for (String path : paths) { 122 is = tryPath(path + libname); 123 if (is != null) { 124 logger.debug("Found " + libname + " in " + path); 125 libpath = path; 126 break; 127 } 128 } 129 return is; 130 } 131 132 /** 133 * Translate all those Windows to "Windows". ("Windows XP", "Windows Vista", "Windows 7", etc.) 134 */ 135 private String unifyOSName(String osname) { 136 if (osname.startsWith("Windows")) { 137 return "Windows"; 138 } 139 return osname; 140 } 141 142 /** 143 * Compute the path to the library. The path is basically 144 * "/" + os.name + "/" + os.arch + "/" + libname. 145 */ 146 private String fatJarLibraryPath(String linkage, String flavor) { 147 String sep = "/"; //System.getProperty("file.separator"); 148 String os_name = unifyOSName(System.getProperty("os.name")); 149 String os_arch = System.getProperty("os.arch"); 150 String path = sep + "lib" + sep + linkage + sep + os_name + sep + os_arch + sep; 151 if (null != flavor) 152 path += flavor + sep; 153 return path; 154 } 155 156 /** 157 * Full path without the OS name non-unified. 158 */ 159 private String fatJarLibraryPathNonUnified(String linkage, String flavor) { 160 String sep = "/"; //System.getProperty("file.separator"); 161 String os_name = System.getProperty("os.name"); 162 String os_arch = System.getProperty("os.arch"); 163 String path = sep + "lib" + sep + linkage + sep + os_name + sep + os_arch + sep; 164 if (null != flavor) 165 path += flavor + sep; 166 return path; 167 } 168 169 /** 170 * Try to open a file at the given position. 171 */ 172 private InputStream tryPath(String path) { 173 Logger.getLogger().debug("Trying path \"" + path + "\"."); 174 return getClass().getResourceAsStream(path); 175 } 176 177 private File createTempFile(String prefix, String suffix, boolean noPrefix) throws IOException { 178 File tempfile = File.createTempFile(prefix, suffix); 179 if (noPrefix == true) { 180 return new File(tempfile.getParentFile(), suffix); 181 } else { 182 return tempfile; 183 } 184 } 185 186 /** 187 * Load a system library from a stream. Copies the library to a temp file 188 * and loads from there. 189 * 190 * @param libname name of the library (just used in constructing the library name) 191 * @param is InputStream pointing to the library 192 */ 193 private void loadLibraryFromStream(String libname, InputStream is, boolean noPrefix) { 194 try { 195 File tempfile = createTempFile("jblas", libname, noPrefix); 196 tempfile.deleteOnExit(); 197 OutputStream os = new FileOutputStream(tempfile); 198 199 logger.debug("tempfile.getPath() = " + tempfile.getPath()); 200 201 long savedTime = System.currentTimeMillis(); 202 203 // Leo says 8k block size is STANDARD ;) 204 byte buf[] = new byte[8192]; 205 int len; 206 while ((len = is.read(buf)) > 0) { 207 os.write(buf, 0, len); 208 } 209 210 double seconds = (double) (System.currentTimeMillis() - savedTime) / 1e3; 211 logger.debug("Copying took " + seconds + " seconds."); 212 213 os.close(); 214 215 logger.debug("Loading library from " + tempfile.getPath() + "."); 216 System.load(tempfile.getPath()); 217 } catch (IOException io) { 218 logger.error("Could not create the temp file: " + io.toString() + ".\n"); 219 } catch (UnsatisfiedLinkError ule) { 220 logger.error("Couldn't load copied link file: " + ule.toString() + ".\n"); 221 throw ule; 222 } 223 } 224}