package com.pi4j.util; /* * #%L * ********************************************************************** * ORGANIZATION : Pi4J * PROJECT : Pi4J :: Java Library (Core) * FILENAME : NativeLibraryLoader.java * * This file is part of the Pi4J project. More information about * this project can be found here: http://www.pi4j.com/ * ********************************************************************** * %% * Copyright (C) 2012 - 2013 Pi4J * %% * Licensed under the Apache License, Version 2.0 (the "License"); you * may not use this file except in compliance with the License. You may obtain a copy of the License * at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. * #L% */ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; import java.util.logging.Level; import java.util.logging.Logger; import com.pi4j.system.SystemInfo; public class NativeLibraryLoader { private static List<String> loadedLibraries = null; private static Logger logger = Logger.getLogger("com.pi4j.util.NativeLibraryLoader"); private static FileHandler fileHandler; private static ConsoleHandler consoleHandler; private static boolean initialized = false; // private constructor private NativeLibraryLoader() { // forbid object construction } public static synchronized void load(String libraryName) { load(libraryName, null); } public static synchronized void load(String libraryName, String fileName) { // check for debug property; if found enabled all logging levels if (initialized == false) { initialized = true; String debug = System.getProperty("pi4j.debug"); if (debug != null) { logger.setLevel(Level.ALL); try { // create an appending file handler fileHandler = new FileHandler("pi4j.log"); fileHandler.setLevel(Level.ALL); consoleHandler = new ConsoleHandler(); consoleHandler.setLevel(Level.ALL); // add to the desired loggers logger.addHandler(fileHandler); logger.addHandler(consoleHandler); } catch (IOException e) {} } } // debug if (fileName == null || fileName.length() == 0) { logger.fine("Load library [" + libraryName + "] (no alternate embedded file provided)"); } else { logger.fine("Load library [" + libraryName + "] (alternate embedded file: " + fileName + ")"); } // create instance if null if (loadedLibraries == null) { loadedLibraries = Collections.synchronizedList(new ArrayList<String>()); } // first, make sure that this library has not already been previously loaded if (loadedLibraries.contains(libraryName)) { // debug logger.fine("Library [" + libraryName + "] has already been loaded; no need to load again."); } else { // --------------------------------------------- // ATTEMPT LOAD FROM SYSTEM LIBS // --------------------------------------------- // assume library loaded successfully, add to tracking collection loadedLibraries.add(libraryName); try { // debug logger.fine("Attempting to load library [" + libraryName + "] using the System.loadLibrary(name) method."); // attempt to load the native library from the system classpath loader System.loadLibrary(libraryName); // debug logger.fine("Library [" + libraryName + "] loaded successfully using the System.loadLibrary(name) method."); } catch (UnsatisfiedLinkError e) { // if a filename was not provided, then throw exception if (fileName == null) { // debug 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."); // library load failed, remove from tracking collection loadedLibraries.remove(libraryName); throw e; } // debug 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."); // --------------------------------------------- // ATTEMPT LOAD BASED ON EDUCATED GUESS OF ABI // --------------------------------------------- // check for system properties boolean armhf_force = false; if(System.getProperty("pi4j.armhf") != null) armhf_force = true; boolean armel_force = false; if(System.getProperty("pi4j.armel") != null) armel_force = true; URL resourceUrl; // first attempt to determine if we are running on a hard float (armhf) based system if(armhf_force) { // attempt to get the native library from the JAR file in the 'lib/hard-float' directory resourceUrl = NativeLibraryLoader.class.getResource("/lib/hard-float/" + fileName); } else if(armel_force){ // attempt to get the native library from the JAR file in the 'lib/soft-float' directory resourceUrl = NativeLibraryLoader.class.getResource("/lib/soft-float/" + fileName); } else { logger.fine("AUTO-DETECTED HARD-FLOAT ABI : " + SystemInfo.isHardFloatAbi()); if(SystemInfo.isHardFloatAbi()) { // attempt to get the native library from the JAR file in the 'lib/hard-float' directory resourceUrl = NativeLibraryLoader.class.getResource("/lib/hard-float/" + fileName); } else { // attempt to get the native library from the JAR file in the 'lib/soft-float' directory resourceUrl = NativeLibraryLoader.class.getResource("/lib/soft-float/" + fileName); } } try { // load library file from embedded resource loadLibraryFromResource(resourceUrl, libraryName, fileName); // debug logger.fine("Library [" + libraryName + "] loaded successfully using embedded resource file: [" + resourceUrl.toString() + "]"); } catch(Exception|UnsatisfiedLinkError ex) { // --------------------------------------------- // ATTEMPT LOAD BASED USING HARD-FLOAT (armhf) // --------------------------------------------- // attempt to get the native library from the JAR file in the 'lib/hard-float' directory URL resourceUrlHardFloat = NativeLibraryLoader.class.getResource("/lib/hard-float/" + fileName); try { // load library file from embedded resource loadLibraryFromResource(resourceUrlHardFloat, libraryName, fileName); // debug logger.info("Library [" + libraryName + "] loaded successfully using embedded resource file: [" + resourceUrlHardFloat.toString() + "] (ARMHF)"); } catch(UnsatisfiedLinkError ule_hard_float) { // debug logger.fine("Failed to load library [" + libraryName + "] using the System.load(file) method using embedded resource file: [" + resourceUrlHardFloat.toString() + "]"); // --------------------------------------------- // ATTEMPT LOAD BASED USING SOFT-FLOAT (armel) // --------------------------------------------- // attempt to get the native library from the JAR file in the 'lib/soft-float' directory URL resourceUrlSoftFloat = NativeLibraryLoader.class.getResource("/lib/soft-float/" + fileName); try { // load library file from embedded resource loadLibraryFromResource(resourceUrlSoftFloat, libraryName, fileName); // debug logger.info("Library [" + libraryName + "] loaded successfully using embedded resource file: [" + resourceUrlSoftFloat.toString() + "] (ARMEL)"); } catch (Throwable err) { // debug logger.severe("Failed to load library [" + libraryName + "] using the System.load(file) method using embedded resource file: [" + resourceUrlSoftFloat.toString() + "]"); logger.throwing(logger.getName(), "load", err); // library load failed, remove from tracking collection loadedLibraries.remove(libraryName); logger.severe("ERROR: The native library [" + libraryName + " : " + fileName + "] 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."); } } catch (Exception ex_hard_float) { // debug logger.severe("Failed to load library [" + libraryName + "] using the System.load(file) method using embedded resource file: [" + resourceUrlHardFloat.toString() + "]"); logger.throwing(logger.getName(), "load", ex_hard_float); // library load failed, remove from tracking collection loadedLibraries.remove(libraryName); logger.severe("ERROR: The native library [" + libraryName + " : " + fileName + "] 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."); } } } } } private static void loadLibraryFromResource(URL resourceUrl, String libraryName, String fileName) throws UnsatisfiedLinkError, Exception { // create a 1Kb read buffer byte[] buffer = new byte[1024]; int byteCount = 0; // debug logger.fine("Attempting to load library [" + libraryName + "] using the System.load(file) method using embedded resource file: [" + resourceUrl.toString() + "]"); // open the resource file stream InputStream inputStream = resourceUrl.openStream(); // get the system temporary directory path File tempDirectory = new File(System.getProperty("java.io.tmpdir")); // check to see if the temporary path exists if (!tempDirectory.exists()) { // debug logger.warning("The Java system temporary path [" + tempDirectory.getAbsolutePath() + "] does not exist."); // instead of the system defined temporary path, let just use the application path tempDirectory = new File(""); } // create a temporary file to copy the native library content to File tempFile = new File(tempDirectory.getAbsolutePath() + "/" + fileName); // make sure that this temporary file does not exist; if it does then delete it if (tempFile.exists()) { // debug logger.warning("The temporary file already exists [" + tempFile.getAbsolutePath() + "]; attempting to delete it now."); // delete file immediately tempFile.delete(); } // create output stream object OutputStream outputStream = null; try { // create the new file outputStream = new FileOutputStream(tempFile); } catch(FileNotFoundException fnfe) { // error logger.severe("The temporary file [" + tempFile.getAbsolutePath() + "] cannot be created; it is a directory, not a file."); throw(fnfe); } catch(SecurityException se) { // error logger.severe("The temporary file [" + tempFile.getAbsolutePath() + "] cannot be created; a security exception was detected. " + se.getMessage()); throw(se); } try { // copy the library file content while ((byteCount = inputStream.read(buffer)) >= 0) { outputStream.write(buffer, 0, byteCount); } // flush all write data from stream outputStream.flush(); // close the output stream outputStream.close(); } catch(IOException ioe) { // error logger.severe("The temporary file [" + tempFile.getAbsolutePath() + "] could not be written to; an IO exception was detected. " + ioe.getMessage()); throw(ioe); } // close the input stream inputStream.close(); try { // attempt to load the new temporary library file System.load(tempFile.getAbsolutePath()); try { // ensure that this temporary file is removed when the program exits tempFile.deleteOnExit(); } catch(SecurityException dse) { // warning logger.warning("The temporary file [" + tempFile.getAbsolutePath() + "] cannot be flagged for removal on program termination; a security exception was detected. " + dse.getMessage()); } } catch(UnsatisfiedLinkError ule) { // if unable to load the library and the temporary file // exists; then delete the temporary file immediately if(tempFile.exists()) tempFile.delete(); throw(ule); } catch(Exception ex) { // if unable to load the library and the temporary file // exists; then delete the temporary file immediately if (tempFile.exists()) { tempFile.delete(); } throw(ex); } } }