1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.glass.utils;
  26 
  27 import java.io.File;
  28 import java.net.URI;
  29 import java.security.AccessController;
  30 import java.security.PrivilegedAction;
  31 import java.util.HashSet;
  32 
  33 public class NativeLibLoader {
  34 
  35     private static final HashSet<String> loaded = new HashSet<String>();
  36 
  37     public static synchronized void loadLibrary(String libname) {
  38         if (!loaded.contains(libname)) {
  39             loadLibraryInternal(libname);
  40             loaded.add(libname);
  41         }
  42     }
  43 
  44     private static boolean verbose = false;
  45 
  46     private static boolean usingModules = false;
  47     private static File libDir = null;
  48     private static String libPrefix = "";
  49     private static String libSuffix = "";
  50 
  51     static {
  52         AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
  53             verbose = Boolean.getBoolean("javafx.verbose");
  54             return null;
  55         });
  56     }
  57 
  58     private static String[] initializePath(String propname) {
  59         String ldpath = System.getProperty(propname, "");
  60         String ps = File.pathSeparator;
  61         int ldlen = ldpath.length();
  62         int i, j, n;
  63         // Count the separators in the path
  64         i = ldpath.indexOf(ps);
  65         n = 0;
  66         while (i >= 0) {
  67             n++;
  68             i = ldpath.indexOf(ps, i + 1);
  69         }
  70 
  71         // allocate the array of paths - n :'s = n + 1 path elements
  72         String[] paths = new String[n + 1];
  73 
  74         // Fill the array with paths from the ldpath
  75         n = i = 0;
  76         j = ldpath.indexOf(ps);
  77         while (j >= 0) {
  78             if (j - i > 0) {
  79                 paths[n++] = ldpath.substring(i, j);
  80             } else if (j - i == 0) {
  81                 paths[n++] = ".";
  82             }
  83             i = j + 1;
  84             j = ldpath.indexOf(ps, i);
  85         }
  86         paths[n] = ldpath.substring(i, ldlen);
  87         return paths;
  88     }
  89 
  90     private static void loadLibraryInternal(String libraryName) {
  91         // Look for the library in the same directory as the jar file
  92         // containing this class.
  93         // If that fails, then try System.loadLibrary.
  94         try {
  95             // FIXME: We should eventually remove this legacy path, since
  96             // it isn't applicable to Jigsaw.
  97             loadLibraryFullPath(libraryName);
  98         } catch (UnsatisfiedLinkError ex) {
  99             if (verbose && !usingModules) {
 100                 System.err.println("WARNING: " + ex);
 101             }
 102 
 103             // NOTE: First attempt to load the libraries from the java.library.path.
 104             // This allows FX to find more recent versions of the shared libraries
 105             // from java.library.path instead of ones that might be part of the JRE
 106             //
 107             String [] libPath = initializePath("java.library.path");
 108             for (int i=0; i<libPath.length; i++) {
 109                 try {
 110                     String path = libPath[i];
 111                     if (!path.endsWith(File.separator)) path += File.separator;
 112                     String fileName = System.mapLibraryName(libraryName);
 113                     File libFile = new File(path + fileName);
 114                     System.load(libFile.getAbsolutePath());
 115                     if (verbose) {
 116                         System.err.println("Loaded " + libFile.getAbsolutePath()
 117                                 + " from java.library.path");
 118                     }
 119                     return;
 120                 } catch (UnsatisfiedLinkError ex3) {
 121                     // Fail silently and try the next directory in java.library.path
 122                 }
 123             }
 124 
 125             // Finally we will use System.loadLibrary.
 126             try {
 127                 System.loadLibrary(libraryName);
 128                 if (verbose) {
 129                     System.err.println("System.loadLibrary("
 130                             + libraryName + ") succeeded");
 131                 }
 132             } catch (UnsatisfiedLinkError ex2) {
 133                 //On iOS we link all libraries staticaly. Presence of library
 134                 //is recognized by existence of JNI_OnLoad_libraryname() C function.
 135                 //If libraryname contains hyphen, it needs to be translated
 136                 //to underscore to form valid C function indentifier.
 137                 if ("iOS".equals(System.getProperty("os.name"))
 138                         && libraryName.contains("-")) {
 139                     libraryName = libraryName.replace("-", "_");
 140                     try {
 141                         System.loadLibrary(libraryName);
 142                         return;
 143                     } catch (UnsatisfiedLinkError ex3) {
 144                         throw ex3;
 145                     }
 146                 }
 147                 // Rethrow exception
 148                 throw ex2;
 149             }
 150         }
 151     }
 152 
 153     /**
 154      * Load the native library from the same directory as the jar file
 155      * containing this class.
 156      */
 157     private static void loadLibraryFullPath(String libraryName) {
 158         try {
 159             if (usingModules) {
 160                 throw new UnsatisfiedLinkError("ignored");
 161             }
 162             if (libDir == null) {
 163                 // Get the URL for this class, if it is a jar URL, then get the
 164                 // filename associated with it.
 165                 String theClassFile = "NativeLibLoader.class";
 166                 Class theClass = NativeLibLoader.class;
 167                 String classUrlString = theClass.getResource(theClassFile).toString();
 168                 if (classUrlString.startsWith("jrt:")) {
 169                     // Suppress warning messages
 170                     usingModules = true;
 171                     throw new UnsatisfiedLinkError("ignored");
 172                 }
 173                 if (!classUrlString.startsWith("jar:file:") || classUrlString.indexOf('!') == -1) {
 174                     throw new UnsatisfiedLinkError("Invalid URL for class: " + classUrlString);
 175                 }
 176                 // Strip out the "jar:" and everything after and including the "!"
 177                 String tmpStr = classUrlString.substring(4, classUrlString.lastIndexOf('!'));
 178                 // Strip everything after the last "/" or "\" to get rid of the jar filename
 179                 int lastIndexOfSlash = Math.max(tmpStr.lastIndexOf('/'), tmpStr.lastIndexOf('\\'));
 180 
 181                 // Set the native directory based on the OS
 182                 String osName = System.getProperty("os.name");
 183                 String relativeDir = null;
 184                 if (osName.startsWith("Windows")) {
 185                     relativeDir = "../bin";
 186                 } else if (osName.startsWith("Mac")) {
 187                     relativeDir = ".";
 188                 } else if (osName.startsWith("Linux")) {
 189                     relativeDir = "./" + System.getProperty("os.arch");
 190                 }
 191 
 192                 // Location of native libraries relative to jar file
 193                 String libDirUrlString = tmpStr.substring(0, lastIndexOfSlash)
 194                         + "/" + relativeDir;
 195                 libDir = new File(new URI(libDirUrlString).getPath());
 196 
 197                 // Set the lib prefix and suffix based on the OS
 198                 if (osName.startsWith("Windows")) {
 199                     libPrefix = "";
 200                     libSuffix = ".dll";
 201                 } else if (osName.startsWith("Mac")) {
 202                     libPrefix = "lib";
 203                     libSuffix = ".dylib";
 204                 } else if (osName.startsWith("Linux")) {
 205                     libPrefix = "lib";
 206                     libSuffix = ".so";
 207                 }
 208             }
 209 
 210             File libFile = new File(libDir, libPrefix + libraryName + libSuffix);
 211             String libFileName = libFile.getCanonicalPath();
 212             try {
 213                 System.load(libFileName);
 214                 if (verbose) {
 215                     System.err.println("Loaded " + libFile.getAbsolutePath()
 216                             + " from relative path");
 217                 }
 218             } catch(UnsatisfiedLinkError ex) {
 219                 throw ex;
 220             }
 221         } catch (Exception e) {
 222             // Throw UnsatisfiedLinkError for best compatibility with System.loadLibrary()
 223             throw (UnsatisfiedLinkError) new UnsatisfiedLinkError().initCause(e);
 224         }
 225     }
 226 }