2014-04-21 85 views
3

我(雖然沒有使用Maven)下面的Standard Directory Layout項目設置:加載本地庫從類路徑

src/main 
     | java 
     | resources 
     | library.dll 

本地DLL位於java文件夾資源文件夾和來源。資源文件夾是Java類路徑的成員。

我現在想要加載DLL而無需設置JRE -Djava.library.path選項或設置PATH變量,以便可以通過簡單的雙擊啓動生成的jar文件。

在運行jar文件時,是否可以將資源文件夾添加到庫搜索路徑而無需執行其他配置? 例如與Manifest中的Class-Path類似的設置?

回答

2

有一箇舊時代的黑客工具,從今天開始仍然有效(1.7.0_55 & 1.8.0_05),允許您使用System.setProperty()對 進行運行時更新,並讓JVM注意到更改。一般情況下,你做到以下幾點:

System.setProperty("java.library.path", yourPath); 
Field sysPath = ClassLoader.class.getDeclaredField("sys_paths"); 
sysPath.setAccessible(true); 
sysPath.set(null, null); 
System.loadLibrary(libraryName); 

谷歌的Java sys_paths並尋找有關這項技術的文章。

小心處理錯誤/異常。 根據需要恢復原始路徑。

+0

不幸的是,這似乎不適用於Java 1.8 – mschrimpf

+1

@Mash - 你也測試過1.7還是1.8?我將我的一個環境升級到1.8.0_05,並且所有工作都很好,可以使用sysPath.set(null,null)對java.library.path進行運行時修改。再看看你的impl。 – Java42

+0

我實際上不能重現它 - 在生成MWE的過程中,我發現如果我創建一個新模塊(使用IntelliJ)並複製其中的舊錯誤模塊的所有內容,它就會起作用。雖然模塊是相同的。 它甚至沒有任何額外的計算,IntelliJ現在似乎妥善處理它。 我很抱歉,但我想這是某種IDE錯誤。 – mschrimpf

3

我已經做了在JNativeHook非常類似的東西,你就需要一些輔助代碼,以確定正確的弓和OS加載代碼(見NativeSystem類)

// The following code covered under the GNU Lesser General Public License v3. 
static { 
    String libName = System.getProperty("jnativehook.lib.name", "JNativeHook"); 

    try { 
     // Try to load the native library assuming the java.library.path was 
     // set correctly at launch. 
     System.loadLibrary(libName); 
    } 
    catch (UnsatisfiedLinkError linkError) { 
     // Get the package name for the GlobalScreen. 
     String basePackage = GlobalScreen.class.getPackage().getName().replace('.', '/'); 

     // Compile the resource path for the 
     StringBuilder libResourcePath = new StringBuilder("/"); 
     libResourcePath.append(basePackage).append("/lib/"); 
     libResourcePath.append(NativeSystem.getFamily()).append('/'); 
     libResourcePath.append(NativeSystem.getArchitecture()).append('/'); 


     // Get what the system "thinks" the library name should be. 
     String libNativeName = System.mapLibraryName(libName); 
     // Hack for OS X JRE 1.6 and earlier. 
     libNativeName = libNativeName.replaceAll("\\.jnilib$", "\\.dylib"); 

     // Slice up the library name. 
     int i = libNativeName.lastIndexOf('.'); 
     String libNativePrefix = libNativeName.substring(0, i) + '-'; 
     String libNativeSuffix = libNativeName.substring(i); 
     String libNativeVersion = null; 

     // This may return null in some circumstances. 
     InputStream libInputStream = GlobalScreen.class.getResourceAsStream(libResourcePath.toString().toLowerCase() + libNativeName); 
     if (libInputStream != null) { 
      try { 
       // Try and load the Jar manifest as a resource stream. 
       URL jarFile = GlobalScreen.class.getProtectionDomain().getCodeSource().getLocation(); 
       JarInputStream jarInputStream = new JarInputStream(jarFile.openStream()); 

       // Try and extract a version string from the Manifest. 
       Manifest manifest = jarInputStream.getManifest(); 
       if (manifest != null) { 
        Attributes attributes = manifest.getAttributes(basePackage); 

        if (attributes != null) { 
         String version = attributes.getValue("Specification-Version"); 
         String revision = attributes.getValue("Implementation-Version"); 

         libNativeVersion = version + '.' + revision; 
        } 
        else { 
         Logger.getLogger(GlobalScreen.class.getPackage().getName()).warning("Invalid library manifest!\n"); 
        } 
       } 
       else { 
        Logger.getLogger(GlobalScreen.class.getPackage().getName()).warning("Cannot find library manifest!\n"); 
       } 
      } 
      catch (IOException e) { 
       Logger.getLogger(GlobalScreen.class.getPackage().getName()).severe(e.getMessage()); 
      } 


      try { 
       // The temp file for this instance of the library. 
       File libFile; 

       // If we were unable to extract a library version from the manifest. 
       if (libNativeVersion != null) { 
        libFile = new File(System.getProperty("java.io.tmpdir"), libNativePrefix + libNativeVersion + libNativeSuffix); 
       } 
       else { 
        libFile = File.createTempFile(libNativePrefix, libNativeSuffix); 
       } 

       byte[] buffer = new byte[4 * 1024]; 
       int size; 

       // Check and see if a copy of the native lib already exists. 
       FileOutputStream libOutputStream = new FileOutputStream(libFile); 

       // Setup a digest... 
       MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); 
       DigestInputStream digestInputStream = new DigestInputStream(libInputStream, sha1); 

       // Read from the digest stream and write to the file steam. 
       while ((size = digestInputStream.read(buffer)) != -1) { 
        libOutputStream.write(buffer, 0, size); 
       } 

       // Close all the streams. 
       digestInputStream.close(); 
       libInputStream.close(); 
       libOutputStream.close(); 

       // Convert the digest from byte[] to hex string. 
       String sha1Sum = new BigInteger(1, sha1.digest()).toString(16).toUpperCase(); 
       if (libNativeVersion == null) { 
        // Use the sha1 sum as a version finger print. 
        libNativeVersion = sha1Sum; 

        // Better late than never. 
        File newFile = new File(System.getProperty("java.io.tmpdir"), libNativePrefix + libNativeVersion + libNativeSuffix); 
        if (libFile.renameTo(newFile)) { 
         libFile = newFile; 
        } 
       } 

       // Set the library version property. 
       System.setProperty("jnativehook.lib.version", libNativeVersion); 

       // Load the native library. 
       System.load(libFile.getPath()); 

       Logger.getLogger(GlobalScreen.class.getPackage().getName()) 
         .info("Library extracted successfully: " + libFile.getPath() + " (0x" + sha1Sum + ").\n"); 
      } 
      catch (IOException e) { 
       throw new RuntimeException(e.getMessage(), e); 
      } 
      catch (NoSuchAlgorithmException e) { 
       throw new RuntimeException(e.getMessage(), e); 
      } 
     } 
     else { 
      Logger.getLogger(GlobalScreen.class.getPackage().getName()) 
        .severe("Unable to extract the native library " + libResourcePath.toString().toLowerCase() + libNativeName + "!\n"); 

      throw new UnsatisfiedLinkError(); 
     } 
    } 
} 
+1

這仍然假設一個硬編碼的資源路徑與'libResourcePath =「/ org/jnativehook/lib /」' - 是類似於類路徑可能的動態?即Eclipse允許你指定一個文件夾作爲類文件夾,它顯然被添加到搜索路徑中。 Manifest類似的東西會很有趣。 – mschrimpf

+1

這將是一個動態包名稱,如果您遍歷整個包樹尋找特定文件名,它將成爲可能。該文件名稱將需要特定於操作系統,並且搜索可能需要很長時間。 (例如:x86_64_windows.bin)請參閱:http://stackoverflow.com/questions/176527/how-can-i-enumerate-all-classes-in-a-package-and-add-them-to-a-list和http://stackoverflow.com/questions/1810614/getting-all-classes-from-a-package –

+1

您的NativeSystem類有一個錯誤。土耳其我問題。您的toString()方法應該使用固定的Locale調用小寫字母。否則「無法提取本機庫/ org/jnativehook/lib/**wındows** /x86/JNativeHook.dll!」 注意小寫土耳其語I,它是「我」而不是「我」 – acheron55