001/* 002 * Class NativeUtils is published under the The MIT License: 003 * 004 * Copyright (c) 2012 Adam Heinrich <adam@adamh.cz> 005 * 006 * Permission is hereby granted, free of charge, to any person obtaining a copy 007 * of this software and associated documentation files (the "Software"), to deal 008 * in the Software without restriction, including without limitation the rights 009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010 * copies of the Software, and to permit persons to whom the Software is 011 * furnished to do so, subject to the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be included in all 014 * copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 022 * SOFTWARE. 023 */ 024package net.gdface.utils; 025 026import java.io.File; 027import java.io.FileNotFoundException; 028import java.io.FileOutputStream; 029import java.io.IOException; 030import java.io.InputStream; 031import java.io.OutputStream; 032import java.net.URL; 033 034/** 035 * 从jar包ä¸åŠ è½½æŒ‡å®šè·¯å¾„ä¸‹çš„åŠ¨æ€åº“<br> 036 * 037 * @see <a href="http://adamheinrich.com/blog/2012/how-to-load-native-jni-library-from-jar">http://adamheinrich.com/blog/2012/how-to-load-native-jni-library-from-jar</a> 038 * @see <a href="https://github.com/adamheinrich/native-utils">https://github.com/adamheinrich/native-utils</a> 039 * 040 */ 041public class NativeUtils { 042 // buffer size used for reading and writing 043 private static final int BUFFER_SIZE = 8192; 044 public static final String NATIVE_FOLDER_PATH_PREFIX = "nativeutils"; 045 046 /** 047 * Temporary directory which will contain the DLLs. 048 */ 049 private static File temporaryDir; 050 051 /** 052 * Private constructor - this class will never be instanced 053 */ 054 private NativeUtils() { 055 } 056 057 /** 058 * Loads library from current JAR archive 059 * @see #copyToTempFromJar(String, Class) 060 */ 061 public static synchronized void loadLibraryFromJar(String path, Class<?> loadClass) throws IOException { 062 File temp = copyToTempFromJar(path,loadClass); 063 System.load(temp.getAbsolutePath()); 064 } 065 /** 066 * copy file from current JAR archive to system temporary directory 067 * 068 * The file from JAR is copied into system temporary directory and then loaded. The temporary file is deleted after 069 * exiting. 070 * Method uses String as filename because the pathname is "abstract", not system-dependent. 071 * 072 * @param path The path of file inside JAR as absolute path (beginning with '/'), e.g. /package/File.ext 073 * @param loadClass class that provide {@link ClassLoader} to load library file by input stream,if null, current class instead. 074 * @throws IOException If temporary file creation or read/write operation fails 075 * @throws IllegalArgumentException If the path is not absolute or if the filename is shorter than three characters 076 * (restriction of {@link File#createTempFile(java.lang.String, java.lang.String)}). 077 * @throws FileNotFoundException If the file could not be found inside the JAR. 078 */ 079 public static synchronized File copyToTempFromJar(String path, Class<?> loadClass) throws IOException { 080 081 if (null == path || !path.startsWith("/")) { 082 throw new IllegalArgumentException("The path has to be absolute (start with '/')."); 083 } 084 085 // Prepare temporary file 086 if (temporaryDir == null) { 087 temporaryDir = createTempDirectory(NATIVE_FOLDER_PATH_PREFIX); 088 temporaryDir.deleteOnExit(); 089 } 090 091 File temp = new File(temporaryDir, path); 092 Class<?> clazz = loadClass == null ? NativeUtils.class : loadClass; 093 InputStream is = clazz.getResourceAsStream(path); 094 try{ 095 copy(is, temp); 096 temp.deleteOnExit(); 097 return temp; 098 } catch (IOException e) { 099 temp.delete(); 100 throw e; 101 } catch (NullPointerException e) { 102 temp.delete(); 103 throw new FileNotFoundException("File " + path + " was not found inside JAR."); 104 } finally { 105 is.close(); 106 } 107 } 108 /** 109 * @see {@link #copyToTempFromJar(String, Class)} 110 */ 111 public static File copyToTempFromJar(String path) throws IOException { 112 return copyToTempFromJar(path, null); 113 } 114 /** 115 * 从jar包ä¸åŠ è½½æŒ‡å®šçš„åŠ¨æ€åº“ 116 * @param path 117 * @throws IOException 118 * @see {@link #loadLibraryFromJar(String, Class)} 119 */ 120 public static void loadLibraryFromJar(String path) throws IOException { 121 loadLibraryFromJar(path,null); 122 } 123 private static File createTempDirectory(String prefix) throws IOException { 124 String tempDir = System.getProperty("java.io.tmpdir"); 125 File generatedDir = new File(tempDir, prefix + System.nanoTime()); 126 if (!generatedDir.mkdir()) 127 throw new IOException("Failed to create temp directory " + generatedDir.getName()); 128 129 return generatedDir; 130 } 131 /** 132 * 从jar包ä¸ä½ç½®'/lib/os_prefix'åŠ è½½æŒ‡å®šåå—的动æ€åº“<br> 133 * os_prefixç”±{@link Platform#getNativeLibraryResourcePrefix()}计算 134 * @param name 库å 135 * @throws IOException 136 * @see {@link #loadLibraryFromJar(String, Class)} 137 */ 138 public static void loadFromJar(String name) throws IOException { 139 String prefix = Platform.getNativeLibraryResourcePrefix(); 140 loadLibraryFromJar("/lib/" + prefix +"/" + System.mapLibraryName(name)); 141 } 142 /** 143 * 将动æ€åº“è§£æžä¸ºå†…部资æºè·¯å¾„ 144 * @param name 145 * @return 146 * @throws IOException 147 */ 148 private static String resolveName(String name) throws IOException { 149 String prefix = Platform.getNativeLibraryResourcePrefix(); 150 return "/lib/" + prefix +"/" + System.mapLibraryName(name); 151 } 152 public static File getTemporaryDir() { 153 return temporaryDir; 154 } 155 private static long copy(InputStream in, File target) throws IOException { 156 target.delete(); 157 File parent = target.getParentFile(); 158 if (parent != null){ 159 parent.mkdirs(); 160 } 161 OutputStream out = new FileOutputStream(target); 162 // do the copy 163 try { 164 return copy(in, out); 165 }finally{ 166 out.close(); 167 } 168 } 169 /** 170 * Reads all bytes from an input stream and writes them to an output stream. 171 */ 172 private static long copy(InputStream source, OutputStream dest) 173 throws IOException 174 { 175 long nread = 0L; 176 byte[] buf = new byte[BUFFER_SIZE]; 177 int n; 178 while ((n = source.read(buf)) > 0) { 179 dest.write(buf, 0, n); 180 nread += n; 181 } 182 return nread; 183 } 184 /** 185 * 从资æºä¸åŠ è½½åŠ¨æ€åº“ 186 * @param name 动æ€åº“å,å‚è§ {@link System#loadLibrary(String)} 187 * @param loaderClass 188 * @throws IOException 189 */ 190 public static void loadLibraryFromResource(String name, Class<?> loaderClass) throws IOException{ 191 if(Platform.isAndroid()){ 192 System.loadLibrary(name); 193 return; 194 } 195 if(null == loaderClass){ 196 loaderClass = NativeUtils.class; 197 } 198 URL url = loaderClass.getResource(resolveName(name)); 199 Assert.notNull(url, "url","not found library"); 200 if(url.getProtocol().equals("file")){ 201 System.load(url.getPath()); 202 return ; 203 } 204 if(url.getProtocol().equals("jar")){ 205 loadFromJar(name); 206 return; 207 } 208 throw new UnsupportedOperationException(); 209 } 210}