我正在維護需要連接到Microsoft SQL Server實例的Java Swing應用程序。出於各種原因,我選擇替換與jTDS一起使用的本機SQL Server驅動程序(前面提到的微軟驅動程序當時不工作,並且在該領域顯然也失敗了)。當我嘗試在IDE外運行可執行文件.jar時,我遇到了問題,因爲我缺少相應的ntlmauth.dll依賴項。手動加載本機庫以繞過限制性環境
開始之前,一定要注意,這個應用程序被開發並在一個非常嚴格的(僅Windows)環境中使用是很重要的:
- 我不能安裝任何軟件需要Windows UAC認證
- 我用戶無法安裝或運行任何需要UAC身份驗證的軟件
- 這意味着我無法將文件寫入System32或JAVA_HOME,也無法使用任何類型的ProcessBuilder tomfoolery來啓動另一個JVM,並使用我需要的任何命令行參數
- 我不能使用可執行包裝/安裝,將只需要在第一次安裝的UAC權限/設置
我試圖解決方案是this one和this one to check it --essentially包裝內該.dll組合.jar,然後解壓縮並在必要時加載 - 因爲我發現的大多數其他解決方案都與上述限制不兼容;然而,即使在本地庫被表面上「加載」之後,我遇到了一個問題,但我得到了一個異常,說它不是。
我的預啓動代碼:
private static final String LIB_BIN = "/lib-bin/";
private static final String JTDS_AUTH = "ntlmauth";
// load required JTDS binaries
static {
logger.info("Attempting to load library {}.dll", JTDS_AUTH);
try {
System.loadLibrary(JTDS_AUTH);
} catch (UnsatisfiedLinkError e) {
loadFromJar();
}
try {
// do some quick checks to make sure that went ok
NativeLibraries nl = new NativeLibraries();
logger.debug("Loaded libraries: {}", nl.getLoadedLibraries().toString());
} catch (NoSuchFieldException ex) {
logger.info("Native library checker load failed", ex);
}
}
/**
* When packaged into JAR extracts DLLs, places these into
*/
private static void loadFromJar() {
// we need to put DLL in temp dir
String path = ***;
loadLib(path, JTDS_AUTH);
}
/**
* Puts library to temp dir and loads to memory
*/
private static void loadLib(String path, String name) {
name = name + ".dll";
try {
// have to use a stream
InputStream in = net.sourceforge.jtds.jdbc.JtdsConnection.class.getResourceAsStream(LIB_BIN + name);
// always write to different location
File fileOut = new File(System.getProperty("java.io.tmpdir") + "/" + path + LIB_BIN + name);
logger.info("Writing dll to: " + fileOut.getAbsolutePath());
OutputStream out = FileUtils.openOutputStream(fileOut);
IOUtils.copy(in, out);
in.close();
out.close();
System.load(fileOut.toString());
} catch (Exception e) {
logger.error("Exception with native library loader", e);
JOptionPane.showMessageDialog(null, "Exception loading native libraries: " + e.getLocalizedMessage(), "Exception", JOptionPane.ERROR_MESSAGE);
}
}
正如你所看到的,我基本上覆制了逐字第一環節的解決方案,有一些小的修改,只是試圖讓應用程序運行。我還從第二個鏈接中複製了該類,並將其命名爲NativeLibraries,該方法的調用相當不重要,但它顯示在日誌中。
反正這裏有上啓動應用程序日誌輸出的相關位:
2015-07-20 12:32:33 INFO - Attempting to load library ntlmauth.dll
2015-07-20 12:32:33 INFO - Writing dll to: C:\Users\***\lib-bin\ntlmauth.dll
2015-07-20 12:32:33 DEBUG - Loaded libraries: [C:\Program Files\Java\jre1.8.0_45\bin\zip.dll, C:\Program Files\Java\jre1.8.0_45\bin\prism_d3d.dll, C:\Program Files\Java\jre1.8.0_45\bin\prism_sw.dll, C:\Program Files\Java\jre1.8.0_45\bin\msvcr100.dll, C:\Program Files\Java\jre1.8.0_45\bin\glass.dll, C:\Program Files\Java\jre1.8.0_45\bin\net.dll, C:\Users\***\lib-bin\ntlmauth.dll]
2015-07-20 12:32:33 INFO - Application startup
***
2015-07-20 12:32:36 ERROR - Database exception
java.sql.SQLException: I/O Error: SSO Failed: Native SSPI library not loaded. Check the java.library.path system property.
at net.sourceforge.jtds.jdbc.TdsCore.login(TdsCore.java:654) ~[jtds-1.3.1.jar:1.3.1]
at net.sourceforge.jtds.jdbc.JtdsConnection.<init>(JtdsConnection.java:371) ~[jtds-1.3.1.jar:1.3.1]
at net.sourceforge.jtds.jdbc.Driver.connect(Driver.java:184) ~[jtds-1.3.1.jar:1.3.1]
at java.sql.DriverManager.getConnection(Unknown Source) ~[na:1.8.0_45]
at java.sql.DriverManager.getConnection(Unknown Source) ~[na:1.8.0_45]
可以看到,圖書館,的確,從日誌中的第三行「加載」(這是最後一項,如果你不喜歡滾動)。然而,我只是簡單地使用了我認爲可能使用本地庫的類(我也試過TdsCore類無濟於事),因爲示例說明了如何做到這一點,只是使用包中的隨機類需要英寸
有什麼我在這裏失蹤?我對JNI或ClassLoaders的內部工作不太熟悉,所以我可能會錯誤地加載它。任何意見或建議將不勝感激!