星期四, 4月 11, 2013

MS JDBC Driver + Proxool

前陣子碰到一個好玩的問題。

狀況:透過 hibernate + proxool + jtds 使用 SSO 方式連 SQL Server 時好時壞。常常出現 SQL Error: 0, SQLState: 08S01 無法連線狀況。
 一聽到此狀況,就先給了建議,改採用 MS 之 JDBC Driver 試試。
後來回報是改採用 MS 的 Driver 情況反而更慘。錯誤就是都無法連線。

所以依此測試結果判斷,就去查看 source code。
結果發現在使用 hibernate 的 session 等處理都是透過 static 變數。
這樣當然會有問題,因 multi-thread 環境下,可能某 thread 已經關閉 session。另一個 thread 還在使用,就會發生問題。

因此請同事考慮 multi-thread 之情況,調整到好。

調整到好之後,問題依舊。

因此再建議改用最陽春的解法,直接透過 JDBC 而不透過 hibernate。
沒想到問題更嚴重了。

但同事後來又試了將 hibernate 由 3.2 換到 3.6 之後,竟然發現ok了。

所以歸咎於是 hibernate 的問題,但此結論實在不能說服我。
且此專案已經近尾聲,更換此 jar 檔等於整個專案都需要重測。
於是,就直接去客戶端實地查看問題。

再測試一遍,發現,hibernate 是有透過 proxool 連 DB,雖然有時會錯,但有連上後,因 Connection Pool 會保留此連線,因此後續就正常。
所以純 JDBC 版本也透過 proxool 來連,情況也與使用 hibernate 情況一致。

但只要換成新版 hibernate,就都正常。
心裡在想,會不會 hibernate 有自己重試連不上就重連機制啊。否則這實在無法解釋。
於是下載了 hibernate source code 來追。不論怎麼追就是無此機制。

但只要無法解釋,心理就會覺得不踏實,因此多試了幾次。
發現,透過新版 hibernate 還是會時好時壞,只是壞的機率低很多。
那就有可能了,可能新版 hibernate 做了最佳化,讓問題出現的機率低很多。
但這不代表問題解決了。

由於只要透過 SQL 認證來連就都不會出錯,改用 Windows SSO 認證才會。
且都是一開始連線就會出錯,還是感覺換成 MS JDBC Driver 應該會比較好。
都是 MS 的產品,應該穩很多吧。

果然換回舊版 hibernate,使用純 JDBC 模式連線,就算不用 Proxool 也非常穩定。
那之前說使用 MS JDBC 不行又是什麼一回事。
原來當時是使用 hibernate 的模式。

再換成 hibernate 模式後果然出錯。出現:
Caused by: java.lang.SecurityException: class "com.microsoft.sqlserver.jdbc.ISQLServerConnection$$FastClassByProxool$$2b8cf6af"'s signer information does not match signer information of other classes in the same package

看到此錯誤,其實就很明顯了。
這是因為 MS 之 JDBC Driver 的 JAR 檔有 sign 過。但卻不符合。
而此是透過 Proxool 才會有此錯誤。
當時建議兩個方案,改採 hibernate 本身陽春之 Connection Pool,或根本不要使用 Connection Pool。果然一換就可正常運作。

另一個方案就是把 MS JDBC Jar 檔內之 sign information 檔刪除。
最後 PM 決定不要動 JAR 檔,因此改採 hibernate 本身之 Connection Pool 來解決。

而在事後,我自己測試時,即將 MS JDBC Jar 檔內之 sign info 檔刪除。
確實透過 Proxool 也可正常連線。

至於為何 proxool 會有此問題呢? 追究其 exception dump,發現其是透過 cglib 去動態 load 物件。所以推測此為 Java 本身的限制,若是動態去 load 物件。而其物件之 Jar 檔是有 sign 過的。則必須驗證其正確性。

那為何當初換 MS JDBC Driver 時出錯沒有找出此問題呢?
因為當時 catch 到 exception 時,輸出到 Logging 沒有輸出 exception stack。
都只看到一行連線錯誤的訊息。錯失提早解決問題的時機。

總結:

  1. 使用 jtds 用 windows 認證 SSO 登入方式連 SQL Server 2005 會時好時壞。連 SQL Server 2008 則很穩定。此結論為推測,因未排除只連 SQL Server 2000 會不會正常。此案子是同時有連 2005 與 2008。但連 2008 都很正常。因此才做此推測。
  2. MS JDBC JAR 為何 signer 無法 match 還沒研究。
  3. 使用 Proxool 連 MS JDBC,須將 JAR 檔內之 sign info 檔案刪除。
  4. 不該疏忽任何 exception stack 輸出。
  5. 對無法解釋的行為,不能妄下結論。





星期三, 4月 10, 2013

Sublime Text 2

最近摸了 Mac,所以會希望找一些可以跨平台的軟體。Mac 與 Windows 都可用的。
經同事介紹了 Sublime Text 2 這套好用的編輯器。
紀錄一些備忘。

Sublime Package Control --> http://wbond.net/sublime_packages/package_control

裝好後,按下 Ctrl + Shift + P 輸入 Install Package 進入 Package 列表安裝

VI mode
  1. Select the Preferences/Settings - Default menu item
  2. Edit the ignored_packages setting, changing it from:
  3.            "ignored_packages": ["Vintage"]
      to:
           "ignored_packages": []
解決ANSI亂碼問題: https://github.com/seanliang/ConvertToUTF8


Xcode 4.6.1 use XVim

由於 Xcode 內的快速鍵需要重新熟悉,且查無還蠻常用的在下一行插入空行的功能。
加上 mac book pro 的鍵盤少了 Home / End 按鍵直接按。頗麻煩。
與其要重新熟悉,不如找一套可以跨平台通吃的快速鍵。
最後找到 Eclipse 與 Xcode 都分別有 Vim 的外掛可以裝。
再加上 Sublime Text 2 本身也支援 Vim 的快速鍵。
雖然已經非常久沒用 Vim,但再重新學起也比重學 Xcode 快速鍵值得。

Xcode 找到的就是 XVim 這套外掛了。

XVim (https://github.com/JugglerShu/XVim)

但此套需要開啟 Xcode 來自行編譯執行來安裝。
因目前也不熟 Xcode ,確實被此折騰了一下。

所以就把編譯發生的問題及解決紀錄下來:

1. "Cocoa/Cocoa.h file not found"
 ==> 需安裝 Command Line Tools 來解決 (https://discussions.apple.com/thread/4236895?start=0&tstart=0)

2. - (NSUInteger)getAddress:(unichar *)parsing:(unichar **)cmdLeft inWindow:(XVimWindow *)window
會出現 'parsing' used as the name of previous parameter rather than as part of the selector 錯誤。

原因出在此 method 第二個參數為暱名參數,在舊版 Xcode 不會出錯。但新版趨於嚴格因此報錯。
解決方法為 parsing 後面多加一個空白。
或是打開 project.pch 檔案加入一行:

#pragma clang diagnostic ignored "-Wmissing-selector-name"

(http://stackoverflow.com/questions/14579197/how-to-disable-new-xcode-4-6-warning-for-whole-project-used-as-the-name-of)

再解決以上兩點後即可 compile 並執行,最後重開 Xcode 即有 Vim 支援了。

HTTPServletRequest POST 取不到資料


最近碰到一個問題,透過 HttpClient POST XML 資料給 Servlet,會一直抓不到資料。
但若透過 REST-Console POST XML 資料給該 Servlet,該 Servlet 又抓得到資料。

問題確實很詭異。

追查到最後,發現是有一隻 filter,呼叫了 request.getParameter("xx") 原因造成。
那為何 request.getParameter 會造成此問題呢?

看看 API 怎麼說明:
If the parameter data was sent in the request body, such as occurs with an HTTP POST request, then reading the body directly via getInputStream() or getReader() can interfere with the execution of this method.

看來送出 POST 由於可能 Request 裡也會有參數,因此就會透過 getInputStream() 來讀取。
因此到該 Servlet 後,再去讀取時,就都抓不到資料了。

那可以解釋為何 HttpClient POST 會抓不到資料,但無法解釋為何透過 REST-Console 來 POST 就可以抓到資料。

最後,查看此兩者所送出之 Header。

HttpClient 會送出 Content-Type: application/x-www-form-urlencoded
而 REST-Console 會送出  Content-Type: application/xml

這就是原因所在了。

由於送出的 header 為 x-www-form-urlencoded。
代表其內容也為 parameter 的格式,自然 getParameter 就會去讀取 Body 內的值。

而 InputStream 讀完了,自然也再讀不出資料來了。