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 - 2016 Pi4J 015 * %% 016 * This program is free software: you can redistribute it and/or modify 017 * it under the terms of the GNU Lesser General Public License as 018 * published by the Free Software Foundation, either version 3 of the 019 * License, or (at your option) any later version. 020 * 021 * This program is distributed in the hope that it will be useful, 022 * but WITHOUT ANY WARRANTY; without even the implied warranty of 023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 024 * GNU General Lesser Public License for more details. 025 * 026 * You should have received a copy of the GNU General Lesser Public 027 * License along with this program. If not, see 028 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 029 * #L% 030 */ 031 032import java.io.File; 033import java.io.FileNotFoundException; 034import java.io.IOException; 035import java.io.InputStream; 036import java.nio.file.Files; 037import java.nio.file.Path; 038import java.nio.file.Paths; 039import java.nio.file.StandardCopyOption; 040import java.util.Set; 041import java.util.TreeSet; 042import java.util.logging.ConsoleHandler; 043import java.util.logging.FileHandler; 044import java.util.logging.Level; 045import java.util.logging.Logger; 046 047public class NativeLibraryLoader { 048 049 private static final Set<String> loadedLibraries = new TreeSet<String>(); 050 private static final Logger logger = Logger.getLogger(NativeLibraryLoader.class.getName()); 051 private static boolean initialized; 052 053 // private constructor 054 private NativeLibraryLoader() { 055 // forbid object construction 056 } 057 058 public static synchronized void load(String fileName) { 059 // check for debug property; if found enable all logging levels 060 if (!initialized) { 061 initialized = true; 062 if (System.getProperty("pi4j.debug") != null) { 063 logger.setLevel(Level.ALL); 064 try { 065 // create an appending file handler 066 FileHandler fileHandler = new FileHandler("pi4j.log"); 067 fileHandler.setLevel(Level.ALL); 068 ConsoleHandler consoleHandler = new ConsoleHandler(); 069 consoleHandler.setLevel(Level.ALL); 070 071 // add to the desired loggers 072 logger.addHandler(fileHandler); 073 logger.addHandler(consoleHandler); 074 } catch (IOException e) { 075 System.err.println("Unable to setup logging to debug. No logging will be done. Error: "); 076 e.printStackTrace(); 077 } 078 } 079 } 080 081 // first, make sure that this library has not already been previously loaded 082 if (loadedLibraries.contains(fileName)) { 083 logger.fine("Library [" + fileName + "] has already been loaded; no need to load again."); 084 return; 085 } 086 087 loadedLibraries.add(fileName); 088 089 // 090 // path = /lib/{platform}/{linking:static|dynamic}/{filename} 091 // 092 String platform = System.getProperty("pi4j.platform", "raspberrypi"); 093 String linking = System.getProperty("pi4j.linking", "static"); 094 String path = "/lib/" + platform + "/" + linking + "/" + fileName; 095 logger.fine("Attempting to load [" + fileName + "] using path: [" + path + "]"); 096 try { 097 loadLibraryFromClasspath(path); 098 logger.fine("Library [" + fileName + "] loaded successfully using embedded resource file: [" + path + "]"); 099 } catch (Exception | UnsatisfiedLinkError e) { 100 logger.log(Level.SEVERE, "Unable to load [" + fileName + "] using path: [" + path + "]", e); 101 // either way, we did what we could, no need to remove now the library from the loaded libraries since we run inside one VM and nothing could possibly change, so there is no point in 102 // trying out this logic again 103 } 104 } 105 106 /** 107 * Loads library from classpath 108 * 109 * The file from classpath is copied into system temporary directory and then loaded. The temporary file is deleted after exiting. Method uses String as filename because the pathname is 110 * "abstract", not system-dependent. 111 * 112 * @param path 113 * The file path in classpath as an absolute path, e.g. /package/File.ext (could be inside jar) 114 * @throws IOException 115 * If temporary file creation or read/write operation fails 116 * @throws IllegalArgumentException 117 * If source file (param path) does not exist 118 * @throws IllegalArgumentException 119 * If the path is not absolute or if the filename is shorter than three characters (restriction of {@see File#createTempFile(java.lang.String, java.lang.String)}). 120 */ 121 public static void loadLibraryFromClasspath(String path) throws IOException { 122 Path inputPath = Paths.get(path); 123 124 if (!inputPath.isAbsolute()) { 125 throw new IllegalArgumentException("The path has to be absolute, but found: " + inputPath); 126 } 127 128 String fileNameFull = inputPath.getFileName().toString(); 129 int dotIndex = fileNameFull.indexOf('.'); 130 if (dotIndex < 0 || dotIndex >= fileNameFull.length() - 1) { 131 throw new IllegalArgumentException("The path has to end with a file name and extension, but found: " + fileNameFull); 132 } 133 134 String fileName = fileNameFull.substring(0, dotIndex); 135 String extension = fileNameFull.substring(dotIndex); 136 137 Path target = Files.createTempFile(fileName, extension); 138 File targetFile = target.toFile(); 139 targetFile.deleteOnExit(); 140 141 try (InputStream source = NativeLibraryLoader.class.getResourceAsStream(inputPath.toString())) { 142 if (source == null) { 143 throw new FileNotFoundException("File " + inputPath + " was not found in classpath."); 144 } 145 Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); 146 } 147 // Finally, load the library 148 System.load(target.toAbsolutePath().toString()); 149 } 150}