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 ---
036    package org.jblas.util;
037    
038    import java.io.*;
039    
040    /**
041     * Class which allows to load a dynamic file as resource (for example, from a 
042     * jar-file)
043     */
044    public class LibraryLoader {
045    
046        private Logger logger;
047        private String libpath;
048        
049        public LibraryLoader() {
050            logger = Logger.getLogger();
051            libpath = null;
052        }
053    
054        /**
055         * <p>Find the library <tt>libname</tt> as a resource, copy it to a tempfile
056         * and load it using System.load(). The name of the library has to be the
057         * base name, it is mapped to the corresponding system name using
058         * System.mapLibraryName(). For example, the library "foo" is called "libfoo.so"
059         * under Linux and "foo.dll" under Windows, but you just have to pass "foo"
060         * the loadLibrary().</p>
061         *
062         * <p>I'm not quite sure if this doesn't open all kinds of security holes. Any ideas?</p>
063         *
064         * <p>This function reports some more information to the "org.jblas" logger at
065         * the FINE level.</p>
066         *
067         * @param libname basename of the library
068         * @throws UnsatisfiedLinkError if library cannot be founds
069         */
070        public void loadLibrary(String libname, boolean withFlavor) {
071            // preload flavor libraries
072            String flavor = null;
073            if (withFlavor) {
074                logger.debug("Preloading ArchFlavor library.");
075                flavor = ArchFlavor.archFlavor();
076            }
077    
078            libname = System.mapLibraryName(libname);
079            logger.debug("Attempting to load \"" + libname + "\".");
080    
081            String[] paths = {
082                "/",
083                "/bin/",
084                fatJarLibraryPath("static", flavor),
085                fatJarLibraryPathNonUnified("static", flavor),
086                fatJarLibraryPath("dynamic", flavor),
087                fatJarLibraryPathNonUnified("dynamic", flavor),
088            };
089    
090            InputStream is = findLibrary(paths, libname);
091    
092            // Oh man, have to get out of here!
093            if (is == null) {
094                throw new UnsatisfiedLinkError("Couldn't find the resource " + libname + ".");
095            }
096    
097            logger.config("Loading " + libname + " from " + libpath + ".");
098            loadLibraryFromStream(libname, is);
099        }
100    
101        private InputStream findLibrary(String[] paths, String libname) {
102            InputStream is = null;
103            for (String path: paths) {
104                is = tryPath(path + libname);
105                if (is != null) {
106                    libpath = path;
107                    break;
108                }
109            }
110            return is;
111        }
112    
113        /** Translate all those Windows to "Windows". ("Windows XP", "Windows Vista", "Windows 7", etc.) */
114        private String unifyOSName(String osname) {
115            if (osname.startsWith("Windows")) {
116                return "Windows";
117            }
118            return osname;
119        }
120    
121        /** Compute the path to the library. The path is basically
122        "/" + os.name + "/" + os.arch + "/" + libname. */
123        private String fatJarLibraryPath(String linkage, String flavor) {
124            String sep = "/"; //System.getProperty("file.separator");
125            String os_name = unifyOSName(System.getProperty("os.name"));
126            String os_arch = System.getProperty("os.arch");
127            String path = sep + "lib" + sep + linkage + sep + os_name + sep + os_arch + sep;
128            if (null != flavor)
129                path += flavor + sep;
130            return path;
131        }
132    
133        /** Full path without the OS name non-unified. */
134        private String fatJarLibraryPathNonUnified(String linkage, String flavor) {
135            String sep = "/"; //System.getProperty("file.separator");
136            String os_name = System.getProperty("os.name");
137            String os_arch = System.getProperty("os.arch");
138            String path = sep + "lib" + sep + linkage + sep + os_name + sep + os_arch + sep;
139            if (null != flavor)
140                path += flavor + sep;
141            return path;
142        }
143    
144        /** Try to open a file at the given position. */
145        private InputStream tryPath(String path) {
146            Logger.getLogger().debug("Trying path \"" + path + "\".");
147            return getClass().getResourceAsStream(path);
148        }
149    
150        /** Load a system library from a stream. Copies the library to a temp file
151         * and loads from there.
152         */
153        private void loadLibraryFromStream(String libname, InputStream is) {
154            try {
155                File tempfile = File.createTempFile("jblas", libname);
156                tempfile.deleteOnExit();
157                OutputStream os = new FileOutputStream(tempfile);
158    
159                logger.debug("tempfile.getPath() = " + tempfile.getPath());
160    
161                long savedTime = System.currentTimeMillis();
162    
163                byte buf[] = new byte[1024];
164                int len;
165                while ((len = is.read(buf)) > 0) {
166                    os.write(buf, 0, len);
167                }
168    
169                double seconds = (double) (System.currentTimeMillis() - savedTime) / 1e3;
170                logger.debug("Copying took " + seconds + " seconds.");
171    
172                os.close();
173    
174                System.load(tempfile.getPath());
175            } catch (IOException io) {
176                logger.error("Could not create the temp file: " + io.toString() + ".\n");
177            } catch (UnsatisfiedLinkError ule) {
178                logger.error("Couldn't load copied link file: " + ule.toString() + ".\n");
179            }
180        }
181    }