2006年6月15日

          原文來自: HttpClient POST 的 UTF-8 編碼問題

          Apache HttpClient ( http://jakarta.apache.org/commons/httpclient/ ) 是一個純 Java 的HTTP 協(xié)議的客戶端編程工具包, 對 HTTP 協(xié)議的支持相當全面, 更多細節(jié)也可以參考IBM 網(wǎng)站上的這篇文章 HttpClient入門 ( http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/ ).

          問題分析

          不過在實際使用中, 還是發(fā)現(xiàn)按照最基本的方式調(diào)用 HttpClient 時, 并不支持 UTF-8 編碼, 在網(wǎng)絡上找過一些文章, 也不得要領, 于是查看了 commons-httpclient-3.0.1 的一些代碼, 首先在 PostMethod 中找到了 generateRequestEntity() 方法:
          ????/**
          ?????*?Generates?a?request?entity?from?the?post?parameters,?if?present.??Calls
          ?????*?{@link?EntityEnclosingMethod#generateRequestBody()}?if?parameters?have?not?been?set.
          ?????*?
          ?????*?@since?3.0
          ?????*/
          ????protected?RequestEntity?generateRequestEntity()?{
          ????????if?(!this.params.isEmpty())?{
          ????????????//?Use?a?ByteArrayRequestEntity?instead?of?a?StringRequestEntity.
          ????????????//?This?is?to?avoid?potential?encoding?issues.??Form?url?encoded?strings
          ????????????//?are?ASCII?by?definition?but?the?content?type?may?not?be.??Treating?the?content
          ????????????//?as?bytes?allows?us?to?keep?the?current?charset?without?worrying?about?how
          ????????????//?this?charset?will?effect?the?encoding?of?the?form?url?encoded?string.
          ????????????String?content?=?EncodingUtil.formUrlEncode(getParameters(),?getRequestCharSet());
          ????????????ByteArrayRequestEntity?entity?=?new?ByteArrayRequestEntity(
          ????????????????EncodingUtil.getAsciiBytes(content),
          ????????????????FORM_URL_ENCODED_CONTENT_TYPE
          ????????????);
          ????????????return?entity;
          ????????}?else?{
          ????????????return?super.generateRequestEntity();
          ????????}
          ????}

          原來使用 NameValuePair 加入的 HTTP 請求的參數(shù)最終都會轉(zhuǎn)化為 RequestEntity 提交到 HTTP 服務器, 接著在 PostMethod 的父類 EntityEnclosingMethod 中找到了如下的代碼:
          ????/**
          ?????*?Returns?the?request's?charset.??The?charset?is?parsed?from?the?request?entity's?
          ?????*?content?type,?unless?the?content?type?header?has?been?set?manually.?
          ?????*?
          ?????*?@see?RequestEntity#getContentType()
          ?????*?
          ?????*?@since?3.0
          ?????*/
          ????public?String?getRequestCharSet()?{
          ????????if?(getRequestHeader("Content-Type")?==?null)?{
          ????????????//?check?the?content?type?from?request?entity
          ????????????//?We?can't?call?getRequestEntity()?since?it?will?probably?call
          ????????????//?this?method.
          ????????????if?(this.requestEntity?!=?null)?{
          ????????????????return?getContentCharSet(
          ????????????????????new?Header("Content-Type",?requestEntity.getContentType()));
          ????????????}?else?{
          ????????????????return?super.getRequestCharSet();
          ????????????}
          ????????}?else?{
          ????????????return?super.getRequestCharSet();
          ????????}
          ????}


          解決方案

          從上面兩段代碼可以看出是 HttpClient 是如何依據(jù) "Content-Type" 獲得請求的編碼(字符集), 而這個編碼又是如何應用到提交內(nèi)容的編碼過程中去的. 按照這個原來, 其實我們只需要重載 getRequestCharSet() 方法, 返回我們需要的編碼(字符集)名稱, 就可以解決 UTF-8 或者其它非默認編碼提交 POST 請求時的亂碼問題了.

          測試

          首先在 Tomcat 的 ROOT WebApp 下部署一個頁面 test.jsp, 作為測試頁面, 主要代碼片段如下:
          <%@?page?contentType="text/html;charset=UTF-8"%>
          <%@?page?session="false"?%>
          <%
          request.setCharacterEncoding("UTF-8");
          String?val?=?request.getParameter("TEXT");
          System.out.println(">>>>?The?result?is?"?+?val);
          %>


          接著寫一個測試類, 主要代碼如下:
          ????public?static?void?main(String[]?args)?throws?Exception,?IOException?{
          ????????String?url?=?"http://localhost:8080/test.jsp";
          ????????PostMethod?postMethod?=?new?UTF8PostMethod(url);
          ????????//填入各個表單域的值
          ????????NameValuePair[]?data?=?{
          ????????????????new?NameValuePair("TEXT",?"中文"),
          ????????};
          ????????//將表單的值放入postMethod中
          ????????postMethod.setRequestBody(data);
          ????????//執(zhí)行postMethod
          ????????HttpClient?httpClient?=?new?HttpClient();
          ????????httpClient.executeMethod(postMethod);
          ????}
          ????
          ????//Inner?class?for?UTF-8?support
          ????public?static?class?UTF8PostMethod?extends?PostMethod{
          ????????public?UTF8PostMethod(String?url){
          ????????????super(url);
          ????????}
          ????????@Override
          ????????public?String?getRequestCharSet()?{
          ????????????//return?super.getRequestCharSet();
          ????????????return?"UTF-8";
          ????????}
          ????}


          運行這個測試程序, 在 Tomcat 的后臺輸出中可以正確打印出 ">>>> The result is 中文" .

          代碼下載

          本文所提到的所有代碼, 以及測試程序(可直接導入 eclipse)提供打包下載: att:HttpClient POST 的 UTF-8 編碼問題.httpClientUTF8.tar.bz2

          END


          posted @ 2006-10-31 23:00 thinkbase.net 閱讀(7722) | 評論 (8)編輯 收藏
           
          原文來自:? 使用 Apache 反向代理實現(xiàn)負載均衡及熱備


          初步設想

          • 早些時候在 JavaEye 上看到過一些使用 lighttpd 或者 apache 作前端, 通過負載均衡, 實現(xiàn)高性能的 Web 系統(tǒng)的討論, 于是留意了一下這方面的技術(shù);
          • 考慮到對不同的 App Server 而言, 實現(xiàn) Session 復制的配置各不相同(通常是需要配置集群), 因此從通用的角度, 覺得使用 session sticky 方式實現(xiàn)的負載均衡比較方便;
          • 由于沒有看到有資料說 lighttpd 能夠?qū)崿F(xiàn) session sticky, 所以決定先使用 Apache 試試.
          參考資料:

          環(huán)境準備

          • 下載安裝 Apache, 測試時使用的是 XAMPP ( http://www.apachefriends.org/en/xampp.html ) 的 Linux 版本 (xampp-linux-1.5.4.tar.gz), 按照安裝說明, 解壓到 /opt/lampp 目錄下就可以使用了;
            • 啟動 Apache: sudo /opt/lampp/lampp startapache
            • 重新加載 Apache: sudo /opt/lampp/lampp reloadapache (在 httpd.conf 文件被修改后可以不重啟, 而是直接 reload 就可以了)
            • 停止服務: sudo /opt/lampp/lampp stop
          • 準備兩個運行同樣程序的 Web 服務器, 這里使用的是 Tomcat 5.5, 并使用一個 jsp 文件作為測試文件(相關源代碼參見文章最后的附件);
            • 這兩個 Tomcat 服務器需要將 HTTP 服務配置在不同的端口上, 同時由于測試時運行在同一臺機器上, 其它端口也需要避免沖突;
          • 下載安裝 JMeter ( jakarta-jmeter-2.2), 用于壓力測試, 驗證負載均衡的效果;

          測試 jsp 文件的說明

          測試用的 jsp 文件 (test.jsp) 具有如下功能:
          • 顯示當前運行的服務器的 IP 地址及端口號, 這樣從返回的頁面就能夠知道是運行在哪一個 Web 服務器上的了;
          • 統(tǒng)計每個客戶端(不同的 session)向同一臺服務器發(fā)出請求的次數(shù), 通過這個計數(shù)可以驗證是否實現(xiàn)了 session sticky;
          • 通過 clear 請求參數(shù)(即 .../test.jsp?clear=1)清除請求次數(shù)的計數(shù)結(jié)果, 以便進行下一次測試;
          • 模擬 JSESSIONID +jvmRoute 的機制, 自行實現(xiàn)了一個 STICK_PORT_TOKEN 的 Cookie, 直接使用不同服務器的 HTTP 端口號作為 route;
            • 說明1: 考慮到方案的通用性, 這里沒有直接使用 JSESSIONID +jvmRoute 的機制;
            • 說明2: 雖然作為一個例子, 相關代碼是寫死在 jsp 文件中的, 但是這個機制可以很方便的用一個 Filter 統(tǒng)一實現(xiàn);

          Apache 的配置

          編輯 Apache 的 httpd.conf 文件(如果使用 xampp-linux 的話, 應該在 /opt/lampp/etc 目錄下), 在文件的最后加上如下內(nèi)容:
          ###############################################################################
          # Reverse Proxy and Load Balance ##############################################
          ###############################################################################
          # 1)簡單的反向代理
          ProxyRequests Off
          <Proxy *>
          Order deny,allow
          Allow from all
          </Proxy>
          ProxyPass /1 http://localhost:8080/test
          #ProxyPassReverse /1 http://localhost:8080/test
          ProxyPass /2 http://localhost:18080/test
          #ProxyPassReverse /2 http://localhost:18080/test
          # 2)非 stickysession 的 balance
          ProxyPass /3 balancer://non-sticky-cluster nofailover=On
          <Proxy balancer://non-sticky-cluster>
          BalancerMember http://localhost:8080/test
          BalancerMember http://localhost:18080/test smax=10
          </Proxy>
          # 3)stickysession 的 balance
          ProxyPass /4 balancer://sticky-cluster stickysession=STICK_PORT_TOKEN nofailover=On
          <Proxy balancer://sticky-cluster>
          BalancerMember http://localhost:8080/test route=8080
          BalancerMember http://localhost:18080/test route=18080 loadfactor=2
          </Proxy>
          這個配置分為3個部分, 包括了 1)簡單的反向代理, 2)非 session sticky 的 load balance, 以及 3)session sticky 的 load balance 三種方式的配置(這里假設兩個 Tomcat 服務器的 HTTP 服務被配置在 8080 和 18080 端口), 其中第 2) 和 3) 的配置中 "nofailover=On" 適合于沒有 session 復制的情況下, 這種情況下, 如果其中一臺 HTTP 服務器出錯, 那么原來分配在這個出錯機器上的瀏覽器客戶端不會被自動轉(zhuǎn)移到另外的服務器上, 必須重新啟動瀏覽器才能將請求分配到另外一臺服務器上去.

          使用 JMeter 測試結(jié)果

          使用 JMeter 對 "3)session sticky 的 load balance" 的效果進行測試, 通過壓力測試的方式, 檢查兩臺 Tomcat 服務器被分配到的請求數(shù)量, 相關的測試腳本參見文章最后的附件.

          注意如果重復測試, 在下一次測試開始之前請對每個 Tomcat 服務器執(zhí)行 .../test.jsp?clear=1 的請求, 清除上一次的計數(shù)結(jié)果.

          從下圖的測試結(jié)果可見: 50個線程中有21個被分配在 8080 端口的服務器上, 29個則被分配到 18080 端口的服務器; 另外, 所有的 session 請求次數(shù)都是 20 次, 說明 session sticky 達到了預期的效果.

          附件


          后記

          如何禁用 XAMPP 自帶的內(nèi)容, 使之成為一個單純的轉(zhuǎn)發(fā)服務器:
          • 1)注釋掉 /opt/lampp/etc/httpd.conf 中 "Include etc/extra/httpd-xampp.conf" 這一行;
          • 2)刪除或者移走 /opt/lampp/htdocs 目錄下的內(nèi)容(但是此目錄需要保留).
          posted @ 2006-10-10 13:12 thinkbase.net 閱讀(7062) | 評論 (7)編輯 收藏
           

          (原文來自: 2006-07-28 介紹一個 RSA 加密小工具 bmrsa)

          因為要做一個不對稱加密/解密相關的應用(其實是想用在軟件的注冊控制方面), 本來的想法是去找找有沒有 RSA 加密/解密的源代碼的例子, 結(jié)果在 sourceforge 找到來 bmrsa(RSA Cryptographic Text Processor), 可以從 http://sourceforge.net/projects/bmrsa 訪問.

          項目 README 里的描述:

           This program is an exercise in prime number generation,
          RSA key generation, RSA encryption and conversion between
          decimal, hexadecimal, base64 and text. Take note that RSA
          is generally not used to encrypt entire messages because it
          is too slow. It is normally used to encrypt keys used in
          other encryption algorithms or other relatively small
          values. For more detailed documentation, execute bmrsa
          from a command line without passing any arguments. You will
          probably want to pipe the output through more like this

          試用了一下, 總結(jié)該軟件有如下特點:
          • 開源, GPL 協(xié)議;
          • 跨平臺, 而且完全使用 C++ 編寫, 下載包里包含了 Windows 和 Linux 下的可執(zhí)行文件;
          • 命令行程序, 運行時使用 stdin 和 stdout, 應該說被其它程序調(diào)用還算方便;
          • 只能處理文本, 不支持中文, 因此對于中文文本或者二進制文件可能需要先 base64 一下.

          我用的是 2003-03-16 16:00 的 bmrsa10.zip 這個包, 可以從項目主頁找到下載, 也可以從下面的鏈接下載:
          下面簡單說明產(chǎn)生 公鑰/私鑰 和 加密/解密 的整個過程:
          • 首先將下載后的 bmrsa10.zip 解壓縮到一個目錄, 進入這個目錄;
          • 如果是 linux 環(huán)境下, 為了后面輸入命令方便, 可以執(zhí)行 export PATH=.:$PATH , 將當前目錄加入 PATH;
          • 首先生成一個 768 位的密鑰文件 _keys.768-bit.txt:
          bmrsa -mkh -g 48 -f _keys.768-bit.txt
          • 接走我們建立另外兩個文本文件 _private-key.768-bit.txt_public-key.768-bit.txt, 分別對應私鑰和公鑰. 這兩個文件都是對 _keys.768-bit.txt 中內(nèi)容的裁減, 基本上類似這個樣子:
            • _private-key.768-bit.txt
          public mod=7ECF58B12DAB4557B9B39589D26CA444BDF96...
          private key=3C4B0676352943057A3B6B0D54A8B0E56265B...
          private p=ED66018402DEED19082ED5EA500B778DAFA7A0...
          private q=88BF09780DC8C15C429AE72AC6F91B0795C4E68...
          • 和:
            • _public-key.768-bit.txt
          public mod=7ECF58B12DAB4557B9B39589D26CA444BDF96...
          public key=BA562B6FEF44681C8937C54FCB985B205DAF0A...
          • 在實際使用中, 我們可以把公鑰公布出來, 而用私鑰加密傳遞的信息, 接收到信息的人可以使用公鑰對信息進行解密(另外也可以反過來使用公鑰加密, 接收方使用私鑰解密);
            • 具體到這個例子中, 也就是說我們可以把 _public-key.768-bit.txt 公開給需要接收我們的信息的人, 而 _private-key.768-bit.txt 必需妥善保管, 因為一旦別人拿到這個私鑰, 那就意味著他可以冒充你發(fā)送消息了;
          • 下面我們建立一個文本文件 _demo.txt 作為加密的原文:
          NAME=thinkbase
          URL =http://www.thinkbase.net
          IP =218.81.120.31
          • 我們使用密鑰 _private-key.768-bit.txt_demo.txt 進行加密, 加密后的文件是 _demo.enc.txt
          bmrsa -mkh -mit -moh -pr -f _private-key.768-bit.txt <_demo.txt >_demo.enc.txt
          • 接收方拿到加密后的文件 _demo.enc.txt 后, 可以使用公鑰 _public-key.768-bit.txt 對信息進行解密:
          bmrsa -mkh -mih -mot -pu -f _public-key.768-bit.txt <_demo.enc.txt

          具體的運行界面參見下圖:




          在實驗過程中使用的一些文件也可以下載, 供參考:
          posted @ 2006-07-29 00:18 thinkbase.net 閱讀(2058) | 評論 (0)編輯 收藏
           

          (原文來自: 2006-06-18 嘗試了一下用 swig 編寫 JNI 程序)

          總是覺得編寫 JNI (Java Native Interface) 程序是件復雜的事情, 于是開始漫無目的地搜索是否有簡單一點的方法, 倒是有些商業(yè)軟件提供這樣的功能, 比如 http://www.jniwrapper.com 就提供了通過 Java 直接調(diào)用 DLL 的功能(可惜是商業(yè)軟件, 哈哈).

          搜索中無意到了 swig 的網(wǎng)站( http://www.swig.org ), 看到一個用 swig 產(chǎn)生 Java 模塊的例子(原來知道 swig 是因為 python 的緣故), 于是就照著例子自己嘗試了一下(比例子稍微復雜一點, 另外我是用 mingw 上的 gcc 進行編譯的).

          源代碼包括 3 個文件, Example.c, Example.i, net/thinkbase/test/Test.java:

          /**?Example.c?****************************************************************/
          #include?<time.h>

          /**A?variable*/
          double? PI?=? 3.1415927 ;

          /**Return?n!*/
          int? fact ( int? n )?{
          ???? if? ( n?<=? 1 )
          ???????? return? 1 ;
          ???? else
          ???????? return? n*fact ( n- 1 ) ;
          }

          /**mod?function*/
          int? mod ( int? x,? int? y )?{
          ???? return? ( x%y ) ;
          }

          /**Get?time?as?String*/
          char? *?getTime (){
          ???? time_t?ltime;
          ???? time ( &ltime ) ;
          ???? return? ctime ( &ltime ) ;
          }

          /**to?upper?case*/
          char? *?toUpperCase ( char? *?result ){
          ???? char? *?p?=?result;
          ???? while ( '\0' !=*p ){
          ???????? char? c?=?*p;
          ???????? if? (?( c?>? 'a' ) && ( c?<? 'x' )?){
          ???????? *p?=?c- 32 ;
          ???????? }
          ???????? p++;
          ???? }
          ???? return? result;
          }


          /**?Example.i?****************************************************************/
          %module?Example
          % {
          ???? /*?Put?header?files?here?or?function?declarations?like?below?*/
          ???? extern? double? PI;
          ???? extern? int? fact ( int? n ) ;
          ???? extern? int? mod ( int? x,? int? y ) ;
          ???? extern? char? *?getTime () ;
          ???? extern? char? *?toUpperCase ( char? *?str ) ;
          % }

          extern? double? PI;
          extern? int? fact ( int? n ) ;
          extern? int? mod ( int? x,? int? y ) ;
          extern? char? *?getTime () ;
          extern? char? *?toUpperCase ( char? *?str ) ;


          /**?net/thinkbase/test/Test.java?*********************************************/
          package? net.thinkbase.test;

          import? net.thinkbase.test.swig.Example;

          public?class? Test? {
          ???? public?static? void? main ( String?argv [])?{
          ???????? System.loadLibrary ( "Example" ) ;
          ???????? System.out.println (
          ???????????? "????Example.getPI()??????=?"? +?Example.getPI ()) ;
          ???????? System.out.println (
          ???????????? "????Example.fact(6)??????=?"? +?Example.fact ( 6 )) ;

          ???????? System.loadLibrary ( "Example" ) ;
          ???????? System.out.println (
          ???????????? "????Example.mod(100,?30)?=?"? +?Example.mod ( 100 ,? 30 )) ;
          ???????? System.out.println (
          ???????????? "????Example.getTime()????=?"? +?Example.getTime ()) ;
          ???????? System.out.println (
          ???????????? "????Example.toUpperCase(\"Hello,?world!\")?=?"? +
          ???????????? Example.toUpperCase ( "Hello,?world!" )) ;
          ???? }
          }



          試驗步驟

          建立必要的目錄:
          • mkdir "net/thinkbase/test/swig"
          • mkdir ".out"
          使用 swig 建立相關接口:
          • swig -java -package net.thinkbase.test.swig -outdir net/thinkbase/test/swig Example.i
          編譯 c 文件得到 dll:
          • gcc -c Example.c -o .out/Example.o -I%JAVA_HOME%/include -I%JAVA_HOME%/include/win32
          • gcc -c Example_wrap.c -o .out/Example_wrap.o -I%JAVA_HOME%/include -I%JAVA_HOME%/include/win32
          • gcc -shared .out/Example.o .out/Example_wrap.o -mno-cygwin -Wl,--add-stdcall-alias -o .out/Example.dll
          編譯測試用的 Java 程序:
          • javac -sourcepath . net/thinkbase/test/Test.java -d .out
          運行測試:
          • java -cp .out -Djava.library.path=.out net.thinkbase.test.Test
          運行及測試結(jié)果:
          源代碼可以從這里下載:
          posted @ 2006-07-29 00:16 thinkbase.net 閱讀(4160) | 評論 (0)編輯 收藏
           
          一個方便的SSL證書配置工具的介紹, 以及Tomcat上配置雙向SSL(就是必須要客戶端數(shù)字證書才能訪問)的具體例子; 文章的主要內(nèi)容完成于一年半以前, 現(xiàn)在發(fā)表基本上算是炒冷飯了, 不過最近加上了 Tomcat 5 和 Tomcat 4 上的實際配置例子, 所以整理了一下重新發(fā)出來.

          配置適用于正式使用環(huán)境下的Tomcat Web服務器雙向SSL認證

          在 Tomcat 5.5 上的配置實例
          在 Tomcat 4.1.31 上的配置實例
          posted @ 2006-06-15 00:16 thinkbase.net 閱讀(5485) | 評論 (14)編輯 收藏
           
          主站蜘蛛池模板: 吉首市| 彭阳县| 桐城市| 油尖旺区| 顺昌县| 河津市| 娱乐| 柳林县| 兴义市| 西畴县| 莱芜市| 大方县| 白玉县| 思南县| 邛崃市| 铜梁县| 武冈市| 安阳县| 仙居县| 南开区| 陇川县| 遂川县| 阳朔县| 乐业县| 佳木斯市| 金门县| 沙河市| 兴安盟| 青铜峡市| 宜都市| 墨江| 资兴市| 朝阳县| 遵义市| 肥乡县| 石狮市| 兴国县| 隆回县| 会宁县| 永康市| 商丘市|