001package com.pi4j.util; 002 003/* 004 * #%L 005 * ********************************************************************** 006 * ORGANIZATION : Pi4J 007 * PROJECT : Pi4J :: Java Library (Core) 008 * FILENAME : NativeLibraryLoader.java 009 * 010 * This file is part of the Pi4J project. More information about 011 * this project can be found here: http://www.pi4j.com/ 012 * ********************************************************************** 013 * %% 014 * Copyright (C) 2012 - 2013 Pi4J 015 * %% 016 * Licensed under the Apache License, Version 2.0 (the "License"); you 017 * may not use this file except in compliance with the License. You may obtain a copy of the License 018 * at 019 * 020 * http://www.apache.org/licenses/LICENSE-2.0 021 * 022 * Unless required by applicable law or agreed to in writing, software distributed under the License 023 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 024 * or implied. See the License for the specific language governing permissions and limitations under 025 * the License. 026 * #L% 027 */ 028 029import java.io.File; 030import java.io.FileNotFoundException; 031import java.io.FileOutputStream; 032import java.io.IOException; 033import java.io.InputStream; 034import java.io.OutputStream; 035import java.net.URL; 036import java.util.ArrayList; 037import java.util.Collections; 038import java.util.List; 039import java.util.logging.ConsoleHandler; 040import java.util.logging.FileHandler; 041import java.util.logging.Level; 042import java.util.logging.Logger; 043 044import com.pi4j.system.SystemInfo; 045 046public class NativeLibraryLoader { 047 048 private static List<String> loadedLibraries = null; 049 private static Logger logger = Logger.getLogger("com.pi4j.util.NativeLibraryLoader"); 050 private static FileHandler fileHandler; 051 private static ConsoleHandler consoleHandler; 052 private static boolean initialized = false; 053 054 // private constructor 055 private NativeLibraryLoader() { 056 // forbid object construction 057 } 058 059 public static synchronized void load(String libraryName) { 060 load(libraryName, null); 061 } 062 063 public static synchronized void load(String libraryName, String fileName) { 064 // check for debug property; if found enabled all logging levels 065 if (initialized == false) { 066 initialized = true; 067 String debug = System.getProperty("pi4j.debug"); 068 if (debug != null) { 069 logger.setLevel(Level.ALL); 070 try { 071 // create an appending file handler 072 fileHandler = new FileHandler("pi4j.log"); 073 fileHandler.setLevel(Level.ALL); 074 consoleHandler = new ConsoleHandler(); 075 consoleHandler.setLevel(Level.ALL); 076 077 // add to the desired loggers 078 logger.addHandler(fileHandler); 079 logger.addHandler(consoleHandler); 080 } 081 catch (IOException e) {} 082 } 083 } 084 085 // debug 086 if (fileName == null || fileName.length() == 0) { 087 logger.fine("Load library [" + libraryName + "] (no alternate embedded file provided)"); 088 } else { 089 logger.fine("Load library [" + libraryName + "] (alternate embedded file: " + fileName + ")"); 090 } 091 // create instance if null 092 if (loadedLibraries == null) { 093 loadedLibraries = Collections.synchronizedList(new ArrayList<String>()); 094 } 095 // first, make sure that this library has not already been previously loaded 096 if (loadedLibraries.contains(libraryName)) { 097 // debug 098 logger.fine("Library [" + libraryName + "] has already been loaded; no need to load again."); 099 } else { 100 // --------------------------------------------- 101 // ATTEMPT LOAD FROM SYSTEM LIBS 102 // --------------------------------------------- 103 104 // assume library loaded successfully, add to tracking collection 105 loadedLibraries.add(libraryName); 106 107 try { 108 // debug 109 logger.fine("Attempting to load library [" + libraryName + "] using the System.loadLibrary(name) method."); 110 111 // attempt to load the native library from the system classpath loader 112 System.loadLibrary(libraryName); 113 114 // debug 115 logger.fine("Library [" + libraryName + "] loaded successfully using the System.loadLibrary(name) method."); 116 } catch (UnsatisfiedLinkError e) { 117 // if a filename was not provided, then throw exception 118 if (fileName == null) { 119 // debug 120 logger.severe("Library [" + libraryName + "] could not be located using the System.loadLibrary(name) method and no embedded file path was provided as an auxillary lookup."); 121 122 // library load failed, remove from tracking collection 123 loadedLibraries.remove(libraryName); 124 throw e; 125 } 126 127 // debug 128 logger.fine("Library [" + libraryName + "] could not be located using the System.loadLibrary(name) method; attempting to resolve the library using embedded resources in the JAR file."); 129 130 131 // --------------------------------------------- 132 // ATTEMPT LOAD BASED ON EDUCATED GUESS OF ABI 133 // --------------------------------------------- 134 135 // check for system properties 136 boolean armhf_force = false; 137 if(System.getProperty("pi4j.armhf") != null) 138 armhf_force = true; 139 boolean armel_force = false; 140 if(System.getProperty("pi4j.armel") != null) 141 armel_force = true; 142 143 URL resourceUrl; 144 145 // first attempt to determine if we are running on a hard float (armhf) based system 146 if(armhf_force) { 147 // attempt to get the native library from the JAR file in the 'lib/hard-float' directory 148 resourceUrl = NativeLibraryLoader.class.getResource("/lib/hard-float/" + fileName); 149 } else if(armel_force){ 150 // attempt to get the native library from the JAR file in the 'lib/soft-float' directory 151 resourceUrl = NativeLibraryLoader.class.getResource("/lib/soft-float/" + fileName); 152 } else { 153 logger.fine("AUTO-DETECTED HARD-FLOAT ABI : " + SystemInfo.isHardFloatAbi()); 154 if(SystemInfo.isHardFloatAbi()) { 155 // attempt to get the native library from the JAR file in the 'lib/hard-float' directory 156 resourceUrl = NativeLibraryLoader.class.getResource("/lib/hard-float/" + fileName); 157 } else { 158 // attempt to get the native library from the JAR file in the 'lib/soft-float' directory 159 resourceUrl = NativeLibraryLoader.class.getResource("/lib/soft-float/" + fileName); 160 } 161 } 162 163 try { 164 // load library file from embedded resource 165 loadLibraryFromResource(resourceUrl, libraryName, fileName); 166 167 // debug 168 logger.fine("Library [" + libraryName + "] loaded successfully using embedded resource file: [" + resourceUrl.toString() + "]"); 169 } 170 171 catch(Exception|UnsatisfiedLinkError ex) { 172 // --------------------------------------------- 173 // ATTEMPT LOAD BASED USING HARD-FLOAT (armhf) 174 // --------------------------------------------- 175 176 // attempt to get the native library from the JAR file in the 'lib/hard-float' directory 177 URL resourceUrlHardFloat = NativeLibraryLoader.class.getResource("/lib/hard-float/" + fileName); 178 179 try { 180 // load library file from embedded resource 181 loadLibraryFromResource(resourceUrlHardFloat, libraryName, fileName); 182 183 // debug 184 logger.info("Library [" + libraryName + "] loaded successfully using embedded resource file: [" + resourceUrlHardFloat.toString() + "] (ARMHF)"); 185 } catch(UnsatisfiedLinkError ule_hard_float) { 186 // debug 187 logger.fine("Failed to load library [" + libraryName + "] using the System.load(file) method using embedded resource file: [" + resourceUrlHardFloat.toString() + "]"); 188 189 // --------------------------------------------- 190 // ATTEMPT LOAD BASED USING SOFT-FLOAT (armel) 191 // --------------------------------------------- 192 193 // attempt to get the native library from the JAR file in the 'lib/soft-float' directory 194 URL resourceUrlSoftFloat = NativeLibraryLoader.class.getResource("/lib/soft-float/" + fileName); 195 196 try { 197 // load library file from embedded resource 198 loadLibraryFromResource(resourceUrlSoftFloat, libraryName, fileName); 199 200 // debug 201 logger.info("Library [" + libraryName + "] loaded successfully using embedded resource file: [" + resourceUrlSoftFloat.toString() + "] (ARMEL)"); 202 } catch (Throwable err) { 203 // debug 204 logger.severe("Failed to load library [" + libraryName + "] using the System.load(file) method using embedded resource file: [" + resourceUrlSoftFloat.toString() + "]"); 205 logger.throwing(logger.getName(), "load", err); 206 207 // library load failed, remove from tracking collection 208 loadedLibraries.remove(libraryName); 209 210 logger.severe("ERROR: The native library [" 211 + libraryName 212 + " : " 213 + fileName 214 + "] could not be found in the JVM library path nor could it be loaded from the embedded JAR resource file; you may need to explicitly define the library path '-Djava.library.path' where this native library can be found."); 215 } 216 } catch (Exception ex_hard_float) { 217 // debug 218 logger.severe("Failed to load library [" + libraryName + "] using the System.load(file) method using embedded resource file: [" + resourceUrlHardFloat.toString() + "]"); 219 logger.throwing(logger.getName(), "load", ex_hard_float); 220 221 // library load failed, remove from tracking collection 222 loadedLibraries.remove(libraryName); 223 224 logger.severe("ERROR: The native library [" 225 + libraryName 226 + " : " 227 + fileName 228 + "] could not be found in the JVM library path nor could it be loaded from the embedded JAR resource file; you may need to explicitly define the library path '-Djava.library.path' where this native library can be found."); 229 } 230 } 231 } 232 } 233 } 234 235 private static void loadLibraryFromResource(URL resourceUrl, String libraryName, String fileName) throws UnsatisfiedLinkError, Exception { 236 // create a 1Kb read buffer 237 byte[] buffer = new byte[1024]; 238 int byteCount = 0; 239 240 // debug 241 logger.fine("Attempting to load library [" + libraryName + "] using the System.load(file) method using embedded resource file: [" + resourceUrl.toString() + "]"); 242 243 // open the resource file stream 244 InputStream inputStream = resourceUrl.openStream(); 245 246 // get the system temporary directory path 247 File tempDirectory = new File(System.getProperty("java.io.tmpdir")); 248 249 // check to see if the temporary path exists 250 if (!tempDirectory.exists()) { 251 // debug 252 logger.warning("The Java system temporary path [" + tempDirectory.getAbsolutePath() + "] does not exist."); 253 254 // instead of the system defined temporary path, let just use the application path 255 tempDirectory = new File(""); 256 } 257 258 // create a temporary file to copy the native library content to 259 File tempFile = new File(tempDirectory.getAbsolutePath() + "/" + fileName); 260 261 // make sure that this temporary file does not exist; if it does then delete it 262 if (tempFile.exists()) { 263 // debug 264 logger.warning("The temporary file already exists [" + tempFile.getAbsolutePath() + "]; attempting to delete it now."); 265 266 // delete file immediately 267 tempFile.delete(); 268 } 269 270 // create output stream object 271 OutputStream outputStream = null; 272 273 try { 274 // create the new file 275 outputStream = new FileOutputStream(tempFile); 276 } catch(FileNotFoundException fnfe) { 277 // error 278 logger.severe("The temporary file [" + tempFile.getAbsolutePath() + "] cannot be created; it is a directory, not a file."); 279 throw(fnfe); 280 } catch(SecurityException se) { 281 // error 282 logger.severe("The temporary file [" + tempFile.getAbsolutePath() + "] cannot be created; a security exception was detected. " + se.getMessage()); 283 throw(se); 284 } 285 286 try { 287 // copy the library file content 288 while ((byteCount = inputStream.read(buffer)) >= 0) { 289 outputStream.write(buffer, 0, byteCount); 290 } 291 292 // flush all write data from stream 293 outputStream.flush(); 294 295 // close the output stream 296 outputStream.close(); 297 } catch(IOException ioe) { 298 // error 299 logger.severe("The temporary file [" + tempFile.getAbsolutePath() + "] could not be written to; an IO exception was detected. " + ioe.getMessage()); 300 throw(ioe); 301 } 302 303 // close the input stream 304 inputStream.close(); 305 306 try { 307 // attempt to load the new temporary library file 308 System.load(tempFile.getAbsolutePath()); 309 310 try { 311 // ensure that this temporary file is removed when the program exits 312 tempFile.deleteOnExit(); 313 } catch(SecurityException dse) { 314 // warning 315 logger.warning("The temporary file [" + tempFile.getAbsolutePath() + "] cannot be flagged for removal on program termination; a security exception was detected. " + dse.getMessage()); 316 } 317 } catch(UnsatisfiedLinkError ule) { 318 // if unable to load the library and the temporary file 319 // exists; then delete the temporary file immediately 320 if(tempFile.exists()) 321 tempFile.delete(); 322 323 throw(ule); 324 } catch(Exception ex) { 325 // if unable to load the library and the temporary file 326 // exists; then delete the temporary file immediately 327 if (tempFile.exists()) { 328 tempFile.delete(); 329 } 330 331 throw(ex); 332 } 333 } 334} 335