<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    很久很久以前

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      34 隨筆 :: 4 文章 :: 17 評論 :: 0 Trackbacks

    2006年8月14日 #

    Winsock 10053錯誤分析

     WSAGetLastError可能會返回10053錯誤,查msdn的解釋是:

    WSAECONNABORTED 10053

    Software caused connection abort.

    An established connection was aborted by the software in your host computer, possibly due to a data transmission time-out or protocol error.

    神馬?軟件原因造成的連接中斷,這是什么意思,不跟沒說一樣的么?
    google一下唄

    Berkeley description:

    A connection abort was caused internal to your host machine. The software caused

    a connection abort because there is no space on the socket’s queue and the socket

     cannot receive further connections.

           

    WinSock description:

    Partly the same as Berkeley. The error can occur when the local network system aborts

    a connection. This would occur if WinSock aborts an established connection after data

    retransmission fails  (receiver never acknowledges data sent on a datastream socket).

           

    TCP/IP scenario:

    A connection will timeout if the local system doesn’t receive an (ACK)nowledgement for

    data sent.  It would also timeout if a (FIN)ish TCP packet is not ACK’d

    (and even if the FIN is ACK’d, it will eventually timeout if a FIN is not returned).

     

    伯克利說這種連接中斷是因為宿主機器的內部原因,因為軟件導致的連接中斷,可能是因為socket的隊列滿并且這個socket不能接收更多的連接了。
    這還不如不說,越說越糊涂了。
    winsocket的描述,似乎還靠譜一些,這種錯誤一般發生在一個建立的連接被重發失敗的情況下產生,接收方沒有響應數據發回來。但還是比較模糊。
    再看看tcp ip標準文檔的說法,如果本地系統沒有收到發送數據的響應(ack)那么這連接就會超時。如果tcp的fin包沒有被ack(或者fin包被ack了但fin沒有返回)那么也會超時。但是,但是,超時跟這個10053有神馬關系?
    再看后續的解釋:
    從參考1中找到如下的描述:

    The Scenario: 
    An HTTP POST is to be sent to an HTTP server.
    The server begins reading the POST and notices that the HTTP request header is invalid.
    It immediately sends an HTTP response (with an error status, perhaps status=400) and closes the connection without trying to continue reading the remainder of the HTTP request that is forthcoming.

    Meanwhile, the client is still happily writing the remainder of the HTTP request to the socket. (Remember a TCP/IP socket connection needs to be closed from both sides. In this case, the server has closed its side, but the client is still pumping data into the half-open connection.)
    The client finishes writing the HTTP POST to the socket — meaning that data has been buffered to Winsock. The client application then tries to read the HTTP response, but it cannot because the outgoing retransmission (of the buffered data by WinSock) failed and the socket connection was shutdown on the client side (by Winsock). Even though the HTTP server sent the response, it is lost and cannot be retrieved. The error your application will receive when
    trying to read the HTTP response on the socket is WSAECONNABORTED. The word "software" in any of the above error messages refers to "WinSock".

    Go back and re-read the original error explanations. Hopefully, after that explanation, you’ll say "Aha! I understand what they’re talking about!".

     

    啊哈,又有http了,大概意思就是http server收到請求了,但發現有問題,那么回一個http錯誤碼,然后就關閉了socket,但與此同時,client端還在很開心地向socket寫數據,注意哦,tcp是全雙工的。client寫完畢后,實際上數據只是放到了發送方的緩沖區中,不一定已經發出去了,如果寫得不好的程序,這個時候就開始從socket讀數據了,這時候就會產生一個WSACONNECTABORTED錯誤,windows上對應的就是10053錯誤。

    但這個解釋實際上是不能讓人滿意的,只是舉出了一種場景,但為什么會產生還沒有解釋。后面又搜到了個參考2,首先解釋10053錯誤是收到fin后client會放棄發送緩沖區中的數據,同時上報錯誤。雖然說法還有點一頭霧水。

    不過這兩個參考給我們一個思路,重現這個問題。

    于是簡單寫個測試用的c-s程序,大概流程如下

     

     

    圖1 CS程序簡化流程圖

    這個簡單程序演示如何出現10053錯誤(以及10054錯誤)。

    如果server在收到client發送的數據后立即關閉socket,那么client再讀時,會收到10053錯誤;如果server收到發送數據后,立即crash,那么隨后client再讀取時會收到10054錯誤。

    ok,能夠重現場景了,那么我們來分析一下更細節的方面,網絡問題自然是抓包,本問題處理抓包還要看一下tcp的狀態以便輔助分析,我們在client端每次操作之前都打印當前的tcp狀態。

    下面是client端發送記錄和對應的netstat情況

    圖2 10053錯誤client端tcp狀態流轉

    client在發送之前tcp狀態是established,在發送之后,server會立即關閉,tcp狀態也變為close_wait,但這只是單方向的關閉,client可以繼續發數據,但client發送后,server立即退出了,導致后續recv會失敗并且返回10053。對應抓包情況如下:



    圖3 10053錯誤client端tcp抓包

    整個通信過程如下:
    1-3.三次握手建立連接
    4.客戶端(10.10.86.93)向服務器端(10.10.86.98)發送數據,1字節
    5.server 中止 發送fin(同時ack之前那個push)
    6.client ack 那個fin
    7.client再發送兩個字節
    8.server此時已經關閉socket,屬于非正常情況,回復復位命令

    整個過程可以重現10053情況,tcp發送分組數據的情況也一目了然,事情到此就可以了么?顯然不是,你也看到了后面還有很多文字,不知此時你心中的問題是否跟我一樣,先說我自己的吧,通過抓包發現這里的異常關閉有個reset,但reset一般是10054(Connection reset by peer)的錯誤,那么10053與10054的區別在哪里。要搞清楚問題也不難,重現場景抓包分析。
    以下是修改上面的cs程序,在client發送的1字節包后,立即crash,這導致的問題是操作系統會立即回收所有資源,包括socket資源。




    圖4 10054錯誤client端tcp狀態流轉

    可以看到在crash之前這個tcp都是established狀態。crash之后,client端接收數據時會收到10054錯誤,場景重現了,我們再看一下抓包情況



    圖5 10054錯誤client端tcp抓包

    這個抓包情況跟10053很像,1-7也同10053,在8時,client收到server發過來的reset,表示當前連接被強制復位了。
    對比10053和10054可以發現,如果srv返回fin標志后再reset那么對應的錯誤就是10053,如果直接reset就是10054錯誤。回過頭來在看參考2中的說法也就有點感覺了。

    總結一下:
    1.遇到不了解的問題,google是非常好的方法
    2.對于一般問題,重現之很重要,可以反復發現問題并驗證問題。自己寫程序或者搭環境盡量重現。
    3.網絡問題抓包是利器,包括各種工具的使用netstat wireshark ping traceroute等。
    4.多重問題對比其中的差異,這里對比10053錯誤和10054錯誤。
    5.理論基礎要搭好,本次問題主要是tcp的異常斷開問題,熟悉tcp斷開的半關閉和復位邏輯,不過理論還是理論,同樣是復位在不同場景下的錯誤碼不同。并且實現上也跟具體的操作系統相關。
    6.實際工作中,
    10053錯誤時,用戶主要是處于透明代理情況,那么這一般是又有用戶所在的代理服務器異常關閉導致的,可能跟我們的離線文件私有協議被用戶所在的代理服務器拒絕掉導致的。

    7.回過頭來在看一開始的解釋,所謂軟件原因造成的連接終端,就是本例子中,server端在shoutdown本方向傳輸時,立即關閉了socket,導致本應該等待對方發送fin來完全結束的正常邏輯被打破,編程單方向強制中止本次tcp,導致client端之后向上報錯,就是所謂的10053錯誤了,這里的軟件就是server端的那個程序。(不過也有種說法是,客戶端發送錯誤數據,導致server端保護機制而強制關閉)


    參考:

    1. http://www.chilkatsoft.com/p/p_299.asp   
    2. http://bbs.csdn.net/topics/360024280#post-361829232
    3. 《TCP/IP詳解(卷一)》18章 TCP連接的建立和中止

     

     

    posted @ 2013-11-28 11:22 Long Long Ago 閱讀(29166) | 評論 (1)編輯 收藏

    最近改造文件傳輸,參考libcurl,考慮到他支持那么多協議,但我只關心http的,所以考慮是否可以只生成http支持的版本,查了一下,果然可以。
    下載,如果不需要最新的,那么只要下載個zip包就好了。
    通過download wizard可以指引你下載不同的版本:http://curl.haxx.se/dlwiz/
    選擇
    source code - 平臺無關- 找到最新版本下載,解壓縮
    編譯比較簡單有build指令
    不過我從vs目錄下找到一個2005版本的vcproj文件,用這個也可以編譯,不過要設置一下include目錄為../../../include
     上面是廢話了,關鍵的怎么值生成對http的支持呢
    只要在編譯指令中增加定義HTTP_ONLY宏就可以了,就這么簡單。
    詳細說明在這里http://curl.haxx.se/docs/install.html
    posted @ 2013-11-21 20:40 Long Long Ago 閱讀(694) | 評論 (0)編輯 收藏

    路由器軟件部分的幾個概念:CFE、固件(Firmware)、NVRAM
    CFE的作用跟PC的BIOS一樣是負責引導操作系統的;固件就是路由器的操作系統,就像PC上的Windows一樣;NVRAM則用于存儲路由器的設置,相當于PC的CMOS。
    當路由器插上電后自動進入CFE,CFE進行類似PC的BIOS那樣進行自檢,自檢通過后就引導路由器的固件了(相當于的PC的Windows了),正常情況下最多一分鐘路由器的固件就會引導完畢,并且Power燈是常亮的。如果Power燈一閃一閃,那一般是CFE沒有引導成功路由器的固件(固件不存在、不正確、已損壞等等)。
    那么我們平常刷固件會把CFE也刷了嗎?呵呵,不會的,就像你平時裝Windows一樣,不管你是裝XP也好,Vista也好,Win7也好,都不會對你的BIOS進行更新。那我想刷CFE該用什么方法?一般用戶用路由器原廠的CFE即可,如果要玩的深入一些想刷CFE,那么可以通過JTAG或者telnet方式來刷(比較危險,而且受固件限制,有些固件不支持)。
    主要是對硬件環境進行初始化,image的更新,加載kernel等
    posted @ 2013-11-13 20:34 Long Long Ago 閱讀(248) | 評論 (0)編輯 收藏

    好久沒有更新這個blog了,java也放下了許久。現在開始重新更新本blog
    posted @ 2010-10-30 13:44 Long Long Ago 閱讀(228) | 評論 (0)編輯 收藏

    本文主要介紹了JXTA中的各種概念。
    1.Peer。一個peer就是實現了一個或多個JXTA協議的網絡設備。比如傳感器,電話, PDA,PC,服務器,巨型機等等。每一個peer與其他peer都是獨立操作并且是異步的。有peer ID來唯一標識一個peer。peer使用JXTA協議公開一個或多個網絡接口(network interface),每一個公開的接口都被廣告為一個peer端點(peer endpoint),這個peer端點唯一標識了一個網絡接口。peer之間不需要有直接的點對點的網絡連接。可以使用中間的peer作為peer的消息路由,將由于硬件網絡或者網絡配置(NATs,防火墻或者代理)而造成的兩個通信peer進行互聯。peer通常被設計成網絡中自然地互相發現,從而構成暫時的或持久的關系成為peer組(peer gorup)。
    2.Peer Group.一個peer組是一個peer的集合,這些peer都有一組相同的服務。peer自組織的加入到peer組中,并通過一個唯一的peer組id來區別這些peer組。每一個peer組都可以建立一個屬于自己的memebership policy,從任何人可以加入到最嚴格的安全驗證以及受保護方式(需要完全的認證書來加入)。一個peer可以同時屬于多于一個peer組。默認地,第一個peer組被實例的組是Net Peer Group。所有的peer都屬于Net Peer Group。peer可以選擇加入其他的peer group。JXTA協議描述了peer如何公開,發現,加入以及監控peer組。
         如下幾點說明了創建peer組的目的。
    1. 建立安全的環境。peer組可以創建一個本地控制域,在這個域中使用一個特定的安全策略。這個安全策略可以簡單的只是一個明文的帳號 /口令交換,也可以像PKI一樣成熟。peer組界定認證的成員訪問和公開受保護的內容(content)。peer組在建立了一個邏輯上的區域,從而對訪問peer組的資源進行界定。
    2. 創建一個范圍環境。peer組裕興建立一個本地的專用域。比如,peer可以組織起來實現一個文檔共享網絡或者一個CPU共享網絡。 peer組提供細分網絡成抽象的區域來提供內在范圍機制(implicit scoping mechanism)。比如,當搜索一個組內容的時,peer組可以界定定義一個搜索范圍。
    3. 創建一個監視環境。peer組允許一個peer為了任何目的去監視一組peer(比如,心跳,traffic introspection或者accountability)。peer組也可以構建一個父子層次結構,其中任何一個組都有一個唯一的父親。查詢請求能偶在這個組中傳播。對于這個組中的廣告也可以在其父組中公開,當然也包裹這個組本身。
    peer組提供了一組服務成為peer組服務。在JXTA中敵營了一個核心peer組服務集合。兩個peer如果要通過一個服務進行交互,他們配需位于同一個peer中。
        核心的組服務有如下:
    1. 發現服務(Discovery Service)。本服務被組中的peer用來查詢per組資源,比如peer,peer組,通道和服務等。
    2. 成員關系服務(Membership Service)。本服務被當前成員用來拒絕或接受一個新的組成員應用。一個peer想要加入到一個group前,首先要確定一個當前的成員,并請求加入。當前成員的集合可以拒絕或者接受某個想加入的應用(application)。本服務可能會發起一個所有peer或者指定組代表的一次投票來決定是否接受或者聚居新的成員應用。
    3. 訪問服務(Access Service)。使用訪問服務可以用來驗證一個peer對另一個peer的請求(request)。如果訪問被允許,那么接受請求的peer會提供給請求peer關于該請求所要知道的信息的信任和相關信息。(注意,在peer組中,不是所有的行為(action)都需要通過訪問服務的檢查,只有那些在某些peer中被限制的行為在需要調用時,才被檢查。)
    4. 管道服務(Pipe Service)。本服務用于在peer組成員間的鏈接管道的建立和管理。
    5. Resolver服務。本服務用來發送一般的查詢請求到其他的peer。peer可以定義和交換請求以便發現任何需要的信息(比如一個服務的狀態或者一個管道端點的信息。)
    6. 監視服務(Monitoring Service)。本服務用來讓一個peer監視本組中的其他成員服務。
        不是所有上面提到的服務都必須要被每個peer實現。一個peer組可以自由的實現那些它認為有用的服務,并可以依賴于默認的Net Peer Group來提供非關鍵核心服務的一般實現。
    3.Network Services。peer之間可以協作并通信以發布,發現和啟動網絡服務。peer可以發布多個服務。peer通過Peer Discovery Protocol來發現network service。在JXTA協議中組織了兩個層次上的network service。
    • Peer Service。一個peer service只有當peer公布了自己的service時才能被訪問。如果這個peer失敗了,那么它的service也失敗了。不同的peer可以運行某個服務的多個實例,但每個實例都必須公開自己的廣告(advertisement).
    • Peer Group Service。peer組服務是這個組中成員所運行服務的實例的集合的組織形式,其中這些實例是互相合作的。如果其中任何一個peer失敗了,那么這個 peer組服務將不會收到影響(假定這個服務可以從其他peer獲得)。peer group服務通過peer 組廣告的以部分來發布。
        服務可以是事先被安裝到peer上或者通過網絡安裝的。為了真正的運行一個服務,peer必須為定位一個適合當前peer運行環境的一個實現。這個從網絡上查找,下載和安裝一個服務的過程很類似于在Internet上搜索一個Web頁面,取回這個頁面然后再安裝需要的插件的過程。
    4.Modules。JXTA的module用來描述任何一段用來實現JXTA world中一個行為的“代碼”的抽象表示(Abstraction)。Network Service就是在一個peer上實現的行為的一個最一般的例子。這個Module Abstraction不一定特指什么“代碼”,它可以是一個Java類,也可以是Java jar,或者動態鏈接庫dll,一個XML消息或是一個腳本。這個module的行為交給了module的實現者。對于一個實例(instance), module表示了一個網絡服務(network service)在不同平臺上的不同實現,這些平臺比如說在java平臺,MS windows平臺,Solaris 平臺。
        Module允許peer實現一個新的行為,通過提供了一個一般的抽象。當peer瀏覽或者加入一個peer組時,他可以查找新的其打算實現的行為。比如,當加入一個peer組后,一個peer可能必須學習新的搜索服務,這個服務只能在本peer組中使用。為了加入到這個組,這個peer必須實現這個新的搜索服務。這個module框架可以啟動平臺無關行為的表示(representation)和廣告(advertisement)。并允許peer描述和實現任何形式這個行為(behavior)的是實現。比如,一個peer使用java或者c實現一個行為的實現。
    & amp; nbsp;   描述和公告一個平臺獨立的行為的能力能有有效的支持peer組包含異構的peer。module的廣告可以使JXTA的peer能夠采用平臺獨立的方式描述一個行為。JXTA平臺使用module廣告來自描述。
        module抽象(Module abstractIon)包括一個module class,module specification和一個moduleimplementation。
    • Module Class。module class主要用于廣告一個行為(behavior)的存在。這個class的定義表述了一個期望的行為和一個期望的對所支持的module的綁定。每一個module class都有一個唯一的ID,成為MoudleClassID
    • Module Specification。Module Specification主要用于訪問這個module。它包含了訪問或者啟動這個module的所有必要的信息。比如,一個服務,他的module specification可能包含了一個用于和其他服務相通信的管道的廣告。一個module specification用于提供module class所指明的功能。對于一個module class可能有多個module specification。每一個module specification都有一個唯一的id,ModuleSpecID。ModuleSpecID包含了ModuleClass ID,并指明了所使用的module class。一個module specification暗含了對網絡的兼容性。對于一個給定的module specification的所有實現都必須使用相同的協議,這些實現都需要是兼容的,即使使用不同的語言實現的。
    • Module Implementation。Module Implement是一給定module specification的實現。對于一個module specification可以有多個module implementation。每一個module implementation都包含了它所實現的與specification相關聯的ModuleSpecID。
        Module可以被peer組服務使用,也可以被獨立的服務所使用。JXTA服務通過module abstraction來區別存在的服務(他的Module Class),服務的specification(Module Specification),或者服務的實現(Service Implementation)。所有的這些都有一個聯合的廣告,并且可以通過其他JXTA peer來公告和發現的。作為一個例子,考慮JXTA的發現服務。它包含一個唯一的ModuleClassID,標識了他作為一個發現服務——他的抽象功能。對于這個發現服務可以有多個不同的規范(Specification),并伴隨著不同的實現。對于組的大小和在網絡中的傳播方式可以使用不同的裁剪策略。每一個Specification都有唯一的MdouleSpecID,其中指明了發現發現服務的ModuleClassID。對于每個規范,都可能有多種實現,每種實現都包含了相同的ModuleSpceID。
        總之,對于一個給定的module Class都可能有多種規范,這些規范可能是完全不同的。然而任何給定規范的所有實現都是假定可以互相兼容的。
    posted @ 2007-05-19 23:11 Long Long Ago 閱讀(950) | 評論 (0)編輯 收藏

    JFace的單獨使用很久都沒有做過,基本上都是在開發elcipse插件時用JFace。今天使用JFace作為Java Application時遇到了 Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/core/runtime/IStatus 錯誤,雖然已經添加了org.eclipse.jface,org.eclipse.core.runtime,org.eclipse.osgi包,但仍然出錯,后來發現org.eclipse.core.runtime.IStatus是在org.eclipse.equonix.common包中,添加后發現 ,還需要添加org.eclipse.core.command包,再添加后搞定。
    posted @ 2007-05-17 14:33 Long Long Ago 閱讀(1052) | 評論 (2)編輯 收藏

        以前實現SWT中的模式和非模式窗體(modal or non-modal window )是通過在子窗體中是否截取父窗體的消息循環來實現的,現在發現這樣好像不行了,但可以通過指定子窗體的樣式(style)來制定子窗體是否是模式窗體。
    Shell child = new Shell(shell,SWT.SHELL_TRIM|SWT.APPLICATION_MODAL);
    上面的語句指定了子窗體child為模式窗體。在上面的style中指定SWT.SHELL_TRIM 是為了顯示窗體的三個默認按鈕(最大,最小和關閉),也可以用SWT.DIALOG_TRIM,不過此時為對話框樣式,只有一個默認按鈕(關閉)。默認的Shell是非模式窗體,并且是有默認系統按鈕的,即樣式為:SWT.SHELL_TRIM|SWT.MODELESS。
    順便說一下,在JFace的Dialog中使用模式對話框只要簡單的設置該對話框setBlockOnOpen(true)即可。
    posted @ 2007-05-17 14:27 Long Long Ago 閱讀(2120) | 評論 (0)編輯 收藏

        在JXTA中分為三個層次,如下圖所示。

         下面分別介紹各個層次:
    1. 平臺層(platform layer)。平臺層即所謂的JXTA核心(JXTA core),專門包裝了最小最精華的部分,這部分主要完成了對P2P網絡最一般的使用。包括,為P2P應用程序的關鍵機制構建模塊,傳輸(包括防火墻穿透),創建peer和peer group以及和安全部分的結合。
    2. 服務層(Service layer)。服務層包含了一些網絡服務,這些服務不一定是P2P程序中必須的,但卻是P2P環境中通常和值得提供的。比如搜索,索引,目錄,存儲系統,文件共享,分布式文件系統,資源聚合和租借等應用服務,以及協議傳輸服務和認證和PKI服務等等。
    3. 應用層(Application layer)。應用層包括了綜合應用的實現,比如P2P的及時消息,文檔和資源的共享,娛樂內容管理和分發,P2P的email系統,分布式拍賣系統以及很多其他的應用。
        實際上,服務層和應用層之間的界限并不是明顯的。一個用戶的應用程序可以作為另一個用戶的服務。整個系統被實際成模塊化的,允許開發者選擇一個服務和應用的集合來定制自己的需求。
        在JXTA中有三個主要方面是它區別一其他分布式網絡模型的:
    1. 使用XML文檔(廣告)來描述網絡資源。
    2. 針對peer間和peer與端點(endpoint)間的抽象管道(abstraction pipe)不需要使用一個可信賴的中心名字/地址認真,比如DNS。
    3. 唯一的peer地址方案(peer IDs)。
    在下面的部分將詳細介紹JXTA的各個組成部分。
    posted @ 2007-05-13 14:13 Long Long Ago 閱讀(805) | 評論 (0)編輯 收藏

        JXTA是一個為P2P計算而開發設計的開發網路計算平臺。它的目標是通過創建基本組件和服務來為peer group創造新的應用。JXTA是juxtpose的簡稱,指并列并排,這里是說P2P的方式和C/S方式以及B/S方式是同等地位了,都是傳統的分布式計算模型。 JXTA為開發應用程序提供了一組開發協議集合和一個開放源碼的參考實現。JXTA協議標準化了peer的風格:
    1. 互相發現;
    2. 在peer group中的自組織;
    3. 廣告和發現網絡服務;
    4. peer間的通訊;
    5. peer間的交互。
        JXTA協議被設計成獨立于程序設計語言和獨立于傳輸協議的。這些協議可以使用Java或者C/C++或者perl實現。同樣,也能在TCP/IP,HTTP,Bluetooth或者其他傳輸協議上實現。JXTA協議使得開發者可以構建和部署P2P的服務或者應用程序,因為這些協議是獨立于程序語言和傳輸協議,所以可以使得采用完全不同軟件體系的異構終端設備能夠交互通訊。使用JXTA技術,開發人員可以開發出基于網絡的,能夠交互的應用程序,這些應用程序有如下特點:
    1. 通過動態查詢和防火墻穿越來發現本網絡中的其他peer;
    2. 任何訪問網絡的節點都可以方便地共享文檔;
    3. 在網絡站點中查找minute content;
    4. 創建一個peer group來提供服務;
    5. 遠端監視peer的行為;
    6. 在網絡中同其他peer進行安全的通信。
    posted @ 2007-05-12 23:59 Long Long Ago 閱讀(856) | 評論 (0)編輯 收藏

    今天打算試試yaws,這是一個使用erlang實現的web服務器,參照blog:
    http://yarivsblog.com/articles/2006/07/12/the-hitchhiker
    首先需要安裝erlang環境,windows和linux下都有,但yaws好像只給了個linux下的安裝文件,于是在ubuntu6.06上安裝。
    從erlang主頁www.erlang.org上下載源碼,編譯步驟:
    tar -xzvf xxx.tar.gz
    cd xxx
    sudo ./configure
    (sudo make clean)#可選的,用于非首次編譯的情況
    sudo make
    sudo make install (as root)
    但configure時出錯:(類似如下)
    configure: error: No curseslibraryfunctions found
    There is a problem with $ERL_TOP/erts/configure not passing the LDFLAGS
    environment variable for test compiles, so ALL library tests fail. I
    modified files $ERL_TOP/erts/aclocal.m4 and $ERL_TOP/configure.in in order
    to locate pthread_create in the standard C runtime library. The
    $ERL_TOP/configure.in produces a configure that works, wheras the
    $ERL_TOP/erts/configure.in does not. At the top of file erts/configure.in:

    AC_PREREQ(2.13)
    AC_INIT(vsn.mk)
    應該是一些curses庫沒有安裝,但apt-get install curses提示找不到,google了一下發現需要安裝
    ncurses-devel包,在網上只找到了相應的rpm包,使用alien命令,將rpm保轉換為deb包安裝:
    sudo apt-get install alien
    sudo?alien?*.rpm,轉成deb后,用dpkg?-i?*.deb
    或者
    sudo?alien?-i?*.rpm
    進行安裝
    (注意,下載rpm包的時候我將包改名了,導致alien時出錯,改成原來的名字就正常了,不知道為什么:P)
    這時候cofigure可以過去了,但make又出錯了,提示erlc找不到,類似如下的錯誤信息:
    erlc -W +debug_info -I../include -o../ebin otp_ring0.erl
    make[4]: erlc: Command not found
    make[4]: ***[../ebin/otp_ring0.beam]Error127
    make[4]: Leaving directory `/usr/src/packages/erlang-10.b.5/lib/kernel/src'
    make[3]: *** [/usr/src/packages/erlang-10.b.5/lib/kernel/ebin/otp_ring0.beam] Error 2
    make[3]: Leaving directory `/usr/src/packages/erlang-10.b.5/erts/emulator'
    make[2]: *** [generate] Error 2
    make[2]: Leaving directory `/usr/src/packages/erlang-10.b.5/erts/emulator'
    make[1]: *** [depend] Error 2
    make[1]: Leaving directory `/usr/src/packages/erlang-10.b.5'
    make: *** [build-stamp] Error 2
    make: *** [debs] Error 2

    看網上有介紹說使用make clean并不完全,
    于是將源碼刪除,重新解壓縮,再configure后,make,沒有提示erlc找不到了,又有提示類似如下的錯誤信息:
    > otp_src_R11B-1/lib/kernel/ebin/erlang.beam > i686-pc-linux-gnu/preload.c
    > m4 -DTARGET=i686-pc-linux-gnu -DOPSYS=linux -DARCH=x86 hipe/
    > hipe_x86_asm.m4 > i686-pc-linux-gnu/opt/plain/hipe_x86_asm.h
    > /bin/sh: m4: command not found
    > make[2]: *** [i686-pc-linux-gnu/opt/plain/hipe_x86_asm.h] Error 127
    > make[2]: Leaving directory `/home/jhancock/otp_src_R11B-1/erts/emulator'
    > make[1]: *** [generate] Error 2
    > make[1]: Leaving directory `/home/jhancock/otp_src_R11B-1/erts/emulator'
    > make: *** [depend] Error 2
    這是m4包沒有找到(http://www.erlang.org/pipermail/erlang-questions/2006-November/023942.html)
    安裝m4包后,繼續make出現如下錯誤:
    hipe/hipe_mkliterals.c:351: error: 'X86_LEAF_WORDS' undeclared here (not in a function)
    hipe/hipe_mkliterals.c:352: error: 'X86_NR_ARG_REGS' undeclared here (not in a function)
    網上搜索結果:http://forum.trapexit.org/viewtopic.php?t=6815
    將源碼刪除,再解壓縮 再make終于過去了
    提示:如下的包最好是在erlang安裝之前就安裝好的: perl, debhelper (>= 4.0.0), autoconf (>= 2.50), openssl, libssl-dev, m4, libncurses5-dev, dpatch, autotools-dev, unixodbc-dev
    make過程大概有1個小時,之后是make install很快。
    打開終端,輸入erl,出現提示符1>
    后面將yaws的安裝和使用
    yaws的安裝挺簡單的,主要是他的編譯是基于erlang的。
    需要注意的是,安裝好后,如果沒有安裝相應的ssl模塊的話,需要更改一下conf文件,將其中關于ssl的部分注釋掉,既如下部分:
    <server xxx>
    ????? ....
    ??? ??? ? <ssl>
    ??? ?? ?? ?? ?? ...
    ??? ?? ?? </ssl>
    </server>
    在windows下的安裝,在yaws的主頁上有說明,需要下載一個bat文件,不過這個文件好像有問題,需要將一些安裝的環境變量改一下,在(SET ERLC_FLAGS=)這行之后添加:
    SET ProgramFiles=G:\erlang\yaws\yaws_program
    SET HOME=G:\erlang\yaws\yaws_data
    SET APPDATA=G:\erlang\yaws\yaws_configure
    ProgramFiles 為yaws的程序位置,HOME為yaws的數據位置,包括www,log,wiki,ssl等,APPDATA為yaws的配置位置。
    如果使用默認安裝參數的話,需要將IF NOT DEFINED ProgramFiles SET ProgramFiles="c:\Program Files"和
    HOME="%HOMEDRIVE%%HOMEPATH%\My Documents"中的雙引號去掉(感覺這個bat文件寫的比較亂)
    同時不要忘了,在安裝好之后注釋掉conf文件中關于ssl的部分。
    安裝好后,默認就可以在%home/www目錄或者/tmp(windows下為%home%\www? or C:\tmp)目錄添加yaws文件(ehtml)來顯示了。
    btw:如果在ubuntu or debian上安裝,最簡單,只要apt-get install yaws 就可以了。運行yaws需要root權限。
    posted @ 2007-01-18 23:22 Long Long Ago 閱讀(4465) | 評論 (2)編輯 收藏

    今天看到一個朋友的Blog, 就忍不住把以前寫的這個代碼拿出來了, 不然這代碼閑著也是閑著. 當然沒有必要照搬全部, 只要中間的那個 zoomImage() 方法即可. 當然還有設置圖片部分透明的方法.

    ?

    /*
    * @(#)BlogMailHandler.java 1.00 2004-10-4
    *
    * Copyright 2004 . All rights reserved.
    * PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
    */
    import java.awt.Color;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.image.BufferedImage;
    import java.io.BufferedInputStream;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.sql.Timestamp;
    import java.util.Properties;

    import javax.imageio.ImageIO;
    import javax.mail.Address;
    import javax.mail.FetchProfile;
    import javax.mail.Flags;
    import javax.mail.Folder;
    import javax.mail.Message;
    import javax.mail.MessagingException;
    import javax.mail.Multipart;
    import javax.mail.Part;
    import javax.mail.Session;
    import javax.mail.Store;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeUtility;

    import studio.beansoft.jsp.StringUtil;
    import com.keypoint.PngEncoder;
    import com.keypoint.PngEncoderB;

    import moblog.*;

    /**
    * BlogMailHandler, 彩E博客郵件的處理程序.
    * 每 15 分鐘更新一次郵件, 發布博客并更新用戶產量.
    * 郵件 POP3 帳戶信息位于 /blogmail.properties 文件中.
    *
    * @author 劉長炯
    * @version 1.00 2004-10-4
    */
    public class BlogMailHandler extends Thread {
    /**
    * @alias 文件根目錄 : String
    */
    private String rootDirectory;

    // 郵件帳戶配置屬性
    private static Properties props = new Properties();

    static {
    try {
    InputStream in = BlogMailHandler.class
    .getResourceAsStream("/blogmail.properties");
    props.load(in);
    in.close();
    } catch (Exception ex) {
    System.err.println("無法加載配置文件 blogmail.properties:"
    + ex.getMessage());
    ex.printStackTrace();
    }
    }

    // 圖像加載的共享實例, 在 Linux 平臺上有可能無法生成圖形對象
    // private static Frame sharedFrame = new Frame();
    private boolean shouldExit = false;

    public BlogMailHandler() {
    }
    /** 定時開始讀取郵件信息 */
    public void startMailProcessCycle() {
    start();
    }
    public void run() {
    while(!shouldExit) {
    doProcess();
    try {
    // 每15分鐘讀取一次
    Thread.currentThread().sleep(60 * 15 * 1000);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    /** 處理進程 */
    private void doProcess() {
    try {
    Store store = openStore();
    Folder inbox = openInbox(store);

    processAllMessages(inbox);
    inbox.close(true);
    store.close();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    protected void finalize() throws Throwable {
    shouldExit = true;
    }
    /**
    * 縮放原始圖片到合適大小.
    *
    * @param srcImage - 原始圖片
    * @return BufferedImage - 處理結果
    */
    private BufferedImage zoomImage(BufferedImage srcImage) {
    int MAX_WIDTH = 100;// TODO: 縮放后的圖片最大寬度
    int MAX_HEIGHT = 160;// TODO: 縮放后的圖片最大高度
    int imageWidth = srcImage.getWidth(null);
    int imageHeight = srcImage.getHeight(null);

    // determine thumbnail size from MAX_WIDTH and MAX_HEIGHT
    int thumbWidth = MAX_WIDTH;
    int thumbHeight = MAX_HEIGHT;
    double thumbRatio = (double)thumbWidth / (double)thumbHeight;
    double imageRatio = (double)imageWidth / (double)imageHeight;
    if (thumbRatio < imageRatio) {
    thumbHeight = (int)(thumbWidth / imageRatio);
    } else {
    thumbWidth = (int)(thumbHeight * imageRatio);
    }
    // 如果圖片小于所略圖大小, 不作處理
    if(imageWidth < MAX_WIDTH && imageHeight < MAX_HEIGHT) {
    thumbWidth = imageWidth;
    thumbHeight = imageHeight;
    }

    // draw original image to thumbnail image object and
    // scale it to the new size on-the-fly (drawImage is quite powerful)
    BufferedImage thumbImage = new BufferedImage(thumbWidth,
    thumbHeight, BufferedImage.TYPE_INT_RGB);
    //thumbImage.getsc
    Graphics2D graphics2D = thumbImage.createGraphics();
    graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    graphics2D.drawImage(srcImage, 0, 0, thumbWidth, thumbHeight, null);
    System.out.println("thumbWidth=" + thumbWidth);
    System.out.println("thumbHeight=" + thumbHeight);
    return thumbImage;
    }

    // Open mail Store
    private Store openStore() throws Exception {
    Store store;
    //--[ Set up the default parameters
    props.put("mail.transport.protocol", "pop");
    props.put("mail.pop.port", "110");
    // props.put("mail.debug", "true");

    Session session = Session.getInstance(props);
    store = session.getStore("pop3");
    // void javax.mail.Service.connect(String host, String user, String
    // password) throws
    // MessagingException
    store.connect(props.getProperty("mail.pop3.host"), props
    .getProperty("username"), props.getProperty("password"));
    return store;
    }

    // Open Inbox
    private Folder openInbox(Store store) throws Exception {
    Folder folder = store.getDefaultFolder();
    if (folder == null) {
    System.out.println("Problem occurred");
    System.exit(1);
    }

    Folder popFolder = folder.getFolder("INBOX");
    popFolder.open(Folder.READ_WRITE);
    return popFolder;
    }

    /** Close mail store. */
    private void closeStore(Store store) {
    try {
    store.close();
    } catch (MessagingException e) {
    e.printStackTrace();
    }
    }

    /**
    * 處理賬號中的所有郵件并刪除這些郵件.
    *
    * @param folder - Folder, 收件箱
    * @throws Exception
    */
    private void processAllMessages(Folder folder) throws Exception {

    Message[] listOfMessages = folder.getMessages();
    FetchProfile fProfile = new FetchProfile();
    fProfile.add(FetchProfile.Item.ENVELOPE);
    folder.fetch(listOfMessages, fProfile);

    for (int i = 0; i < listOfMessages.length; i++) {
    try {
    processSingleMail(listOfMessages[i]);
    } catch (Exception e) {
    e.printStackTrace();
    }

    // Delete mail
    listOfMessages[i].setFlag(Flags.Flag.DELETED, true);
    }
    }

    /**
    * 處理單個 Email, 將文章發表, 并將附件圖片轉換為 PNG 后存入用戶目錄.
    *
    * @param message -
    * Message, email 消息
    */
    private void processSingleMail(Message message) throws Exception {
    BlogContent content = new BlogContent();
    BlogUser user = new BlogUser();
    BlogPicture picture = new BlogPicture();

    // 1. 假定發件人為手機號, 并嘗試根據手機號查找用戶
    Address[] addList = message.getFrom();
    if (addList.length > 0) {
    String userMail = ((InternetAddress) addList[0]).getAddress();
    // 取出 彩E 郵件用戶手機號, 格式: 手機號@someone.com
    String mobileNumber = userMail.substring(0, userMail.indexOf("@"));
    System.out.println("用戶手機號為:" + mobileNumber);
    if (!user.findByMDN(mobileNumber)) {
    // Not found user, then return
    System.err.println("user " + ((InternetAddress) addList[0]).getAddress() + " not found.");
    return;
    }
    }

    // 2. 嘗試讀取郵件正文
    // 復合郵件
    if (message.isMimeType("multipart/*")) {
    // 標記是否處理過圖片
    boolean imageHasProcessed = false;
    Multipart multipart = (Multipart) message.getContent();
    for (int i = 0, n = multipart.getCount(); i < n; i++) {
    // System.err.println("Reading multipart " + i);
    Part part = multipart.getBodyPart(i);
    // System.err.println("ContentType = " + part.getContentType());

    // 3. 處理附件圖片, 只處理第一個圖片
    String disposition = part.getDisposition();
    // System.err.println("disposition = " + disposition);
    if (disposition != null
    && (disposition.equals(Part.ATTACHMENT) || disposition
    .equals(Part.INLINE)) && !imageHasProcessed) {
    // 需要反編碼郵件文件名, 有的郵件的附件的文件名是經過編碼的,
    // 但是 JavaMail 并不能處理出來(BeanSoft, 2004-10-13)
    String fileName = MimeUtility.decodeText(part.getFileName());
    String ext = StringUtil.getExtension(fileName)
    .toLowerCase();
    System.err.println("part.getFileName() = " + fileName);

    if ("gif".equals(ext) || "jpg".equals(ext)
    || "png".equals(ext)) {
    BufferedInputStream dataIn = null;
    // 轉換非 PNG 格式圖片為 PNG 格式 -- 取消
    // if (!"png".equals(ext)) {
    ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
    try {
    // Convert image file to PNG file
    BufferedImage buffImg = ImageIO.read(part
    .getInputStream());
    // Read image file from attachment
    // 縮放圖片
    buffImg = zoomImage(buffImg);

    int imageWidth = buffImg.getWidth(null);
    int imageHeight = buffImg.getHeight(null);
    BufferedImage outImg = new BufferedImage(
    imageWidth, imageHeight,
    // BufferedImage.TYPE_4BYTE_ABGR 是 24 位色深, TYPE_BYTE_INDEXED 是 8 位
    BufferedImage.TYPE_INT_RGB);
    // 使圖片透明
    // embossImage(buffImg, outImg);
    outImg.getGraphics().drawImage(buffImg, 0, 0, imageWidth, imageHeight, null);
    // Save image to PNG output stream
    // ImageIO.write(outImg, "png", pngOut);
    // Save using keypoint PNG encoder
    PngEncoderB pngb = new PngEncoderB(outImg,
    PngEncoder.NO_ALPHA, 0, 9);
    pngOut.write(pngb.pngEncode());
    dataIn = new BufferedInputStream(
    new ByteArrayInputStream(pngOut
    .toByteArray()));
    } catch (Exception e) {
    }
    // } else {
    // dataIn = new BufferedInputStream(part
    // .getInputStream());
    // }
    // All pictures change to png format
    ext = "png"
    // Insert picture info into database
    picture.setBlogID(user.getId());
    picture.setCreationTime(new Timestamp(System
    .currentTimeMillis()));
    picture.setFileEXName(ext);
    picture.setTitle(fileName);
    picture.create();
    // Save png file to user directory, /users/userId/pictureId.png
    FileOutputStream outFile = new FileOutputStream(
    rootDirectory + File.separatorChar + user.getId() + File.separatorChar
    + picture.getId() + "." + ext);
    int c;
    while ((c = dataIn.read()) != -1) {
    outFile.write(c);
    }

    outFile.close();
    imageHasProcessed = true;
    }
    }
    // 純文本郵件, 帶附件
    else if (part.isMimeType("text/plain")) {
    String body = part.getContent().toString();
    String title = message.getSubject();

    content.setBlogID(user.getId());
    content.setCreationTime(new Timestamp(System.currentTimeMillis()));
    content.setTitle(title);
    content.setNote(body);
    }

    // 典型的 HTML 和 文本郵件可選形式, 進一步分析
    if (part.getContent() instanceof Multipart) {
    Multipart subPart = (Multipart) part.getContent();

    for (int j = 0, m = subPart.getCount(); j < m; j++) {
    Part mailText = subPart.getBodyPart(j);

    if (mailText.isMimeType("text/plain")) {
    String body = mailText.getContent().toString();
    String title = message.getSubject();

    content.setBlogID(user.getId());
    content.setCreationTime(new Timestamp(System.currentTimeMillis()));
    content.setTitle(title);
    content.setNote(body);
    break;
    }
    }
    }

    }// End of multipart parse

    // 4. 創建博客記錄
    content.setPictureId(picture.getId());
    if(content.create() > 0) {
    // 更新用戶產量
    user.setPostTimes(user.getPostTimes() + 1);
    user.update();
    }
    }
    // 純文本郵件, 無附件
    else if (message.isMimeType("text/plain")) {
    String body = message.getContent().toString();
    String title = message.getSubject();

    content.setBlogID(user.getId());
    content.setCreationTime(new Timestamp(System.currentTimeMillis()));
    content.setTitle(title);
    content.setNote(body);

    if(content.create() > 0) {
    // 更新用戶產量
    user.setPostTimes(user.getPostTimes() + 1);
    user.update();
    }
    }
    }

    // 測試, 嘗試連接一次到郵件服務器
    public static void main(String[] args) {
    BlogMailHandler handler = new BlogMailHandler();

    // TODO: debug, 請在 JSP 里面設置圖片目錄的根路徑
    handler.rootDirectory = "F:/Moblog/users/"
    handler.doProcess();
    }

    /**
    * @return Returns the rootDirectory.
    */
    public String getRootDirectory() {
    return rootDirectory;
    }

    /**
    * @param rootDirectory
    * The rootDirectory to set.
    */
    public void setRootDirectory(String property1) {
    this.rootDirectory = property1;
    }

    /** Make image transparent */
    private void embossImage(BufferedImage srcImage, BufferedImage destImage) {
    int width = srcImage.getWidth();
    int height = srcImage.getHeight();

    for (int i = 0; i < height; i++) {
    for (int j = 0; j < width; j++) {
    int newColor = handlesinglepixel(j, i, srcImage.getRGB(j, i));
    destImage.setRGB(j, i, newColor);
    }
    }
    }

    // Handle picture single pixel, change 0xff00ff color to transparent
    private int handlesinglepixel(int x, int y, int pixel) {
    int alpha = (pixel >> 24) & 0xff;
    int red = (pixel >> 16) & 0xff;
    int green = (pixel >> 8) & 0xff;
    int blue = (pixel) & 0xff;
    // Deal with the pixel as necessary...
    // alpha 為 0 時完全透明, 為 255 時不透明
    Color back = new Color(0xFF00FF);
    // 將與背景色相同(此處PNG圖片為紫色)的像素點的透明度設為透明
    if (isDeltaInRange(back.getRed(), red, 2)
    && isDeltaInRange(back.getGreen(), green, 2)
    && isDeltaInRange(back.getBlue(), blue, 2)) {
    // System.out.println("x=" + x + "y=" + y + " is transparent.");
    alpha = 0;
    }
    // red = red / 2;
    // //green = green / 2 + 68;
    // blue = blue / 2;

    return alpha << 24 | red << 16 | green << 8 | blue;
    }

    // 判斷兩個整數的差是否在一定范圍之內
    private boolean isDeltaInRange(int first, int second, int range) {
    if (first - second <= range && second - first <= range)
    return true;
    return false;
    }
    }

    • #?re: Java 中收取郵件并自動縮放圖片的代碼(原創)
      冷面閻羅
      Posted @ 2006-12-29 18:31
      不錯!
      自己也寫過java收發郵件的程序!??回復??
    • #?re: Java 中收取郵件并自動縮放圖片的代碼(原創)
      托托姆
      Posted @ 2006-12-30 12:10
      不知道BeanSoft兄是不是看了我昨天的帖子有此感想。。。:)
      我測試了一下BeanSoft兄的zoomImage() 方法,如果按原代碼執行,原本圖片透明的部分將變成黑色。如果修改TYPE_INT_RGB為TYPE_INT_ARGB,則能避免這個問題。
    posted @ 2006-12-30 13:27 Long Long Ago 閱讀(581) | 評論 (0)編輯 收藏

    寫程序中遇到一個問題?如下:
    mySoc?=?new?Socket(svrAddress,5555);
    
    
    myInput?=?new?ObjectInputStream(mySoc.getInputStream());//有問題
    myOutput?=?new?ObjectOutputStream(mySoc.getOutputStream());//有問題
    //myInput?=?new?DataInputStream(mySoc.getInputStream());
    //myOutput?=?new?DataOutputStream(mySoc.getOutputStream());?
    注銷的語句運行可以成功
    但是未注銷的那部分?運行時就卡在那里了
    但是卻沒有拋出異常
    請教原因是什么?有什么問題
    該怎么解決呢?

    找了好久終于再網上找到關于這個問題的說明了?因為問題比較特殊?所以貼出來希望對大家
    有幫助
    
    主機端先建立ObjectInputStream后建立ObjectOutputStream,則對應地客戶端要先建立
    ObjectOutputStream后建立ObjectInputStream,否則會造成兩方互相等待數據而導致死
    鎖。
    
    原因是建立ObjectInputStream對象是需要先接收一定的header數據,接收到這些數據之前
    會處于阻塞狀態。故而為了防止這種死鎖狀態,通訊兩方的
    ObjectInputStraem,ObjectOutputStream必須注意順序對應使用。
    
    
    目前相應的解決辦法還沒有找到?如果要解決?可以嘗試重載對象輸入輸出流
    posted @ 2006-12-30 12:46 Long Long Ago 閱讀(946) | 評論 (1)編輯 收藏

    ?????? Tabbed Property是eclipse3.2中新加入一個view,可以使屬性編輯器的功能近乎無限的擴大。這里說明一些Tabbed Property的使用方法。Tabbed Property中分成三個部分,Contributer,Tabs,Sections,一個Contributor包含若干個Tabs,一個Tabs又可以包含若干個sections。下面我們來分別進行描述。
    ????? 1。Contributor 這需要擴展org.eclipse.ui.views.properties.tabbed.PropertyContributor擴展點,定義時,最重要的是定義contributId,這個id必須是全局唯一的,這樣在加載屬性頁時,才能找到這個我們定義的屬性頁,一般地,我們都將對應于這個屬性頁的workbenchpart的id作為本contributor的id,這樣我們在代碼中可以不硬編碼本id字符串,而使用getSite().getId()就可以得到這個id了(當然,這樣定義id不是必須的)。一個property view可以被多個workbench part共享,但 一個workbench part只能有一個property view,這個workbench part需要實現ITabbedPropertySheetPageContributor?接口,這個接口只有一個方法,要求返回本part對應的tabbed property Contributor id,一般只要return getSite().getId();
    ?? contributor有如下幾個attribute:
    ???1)typeMapper,這個類需要實現org.eclipse.ui.views.properties.tabbed.ITypeMapper,主要是實現類型的映射,因為我們選擇的元素并不一定是實現IPropertySource的元素(即能夠給property view提供內容的元素),比如在GEF中,我們選擇的finger實際上是選擇了對應的EditPart,而實際上實現了IPropertySource一般的是model部分的元素,所以這時候我們要將Editpart映射到對應的model元素。
    ???2)labelProvider,需要一個實現org.eclipse.jface.viewers.ILabelProvider的類,主要是在各個tabs的最上面顯示文字和圖片。
    ???3)propertyCategory,用于聚合多個tabs,注意至少要定義一個category,來聚合tabs,否則,可能會顯示property失敗。

    ???2。Tabs,這個需要擴展org.eclipse.ui.views.properties.tabbed.propertyTabs擴展點,其中contributorId就是與之相關聯的Contributor的id,然后我們可以定義多個tab,這些tab的屬性如下:
    ???1)label,用于顯示在property view的tab bar上的字
    ???2)category,填入的就是在Contributor擴展點中定義的那些category,用于聚合tabs
    ???3)id,本tab的唯一標識
    ???4)afterTab,用于tab之間的排序,如果這是第一個tab,則沒有afterTab,afterTab指的是在本tab之前的那個tab,并且afterTab描述的是在同一個category中的tabs,不同category之間的順序是按照在contributor中定義category的順序來定義的。
    ???5)indented,如果為ture,則各個tabs是有縮進的
    ???6)image,本tab的圖片

    ???3。section ,需要擴展 org.eclipse.ui.views.properties.tabbed.PropertySections擴展點,它的contributionId就是本section所在的Contribution的id,針對每個tab,我們可以定義多個section,每個section的attribut描述如下:
    ???1)id,本secation的唯一標識
    ???2)tab,本section所屬tab的標識
    ???3)class,實現了org.eclipse.ui.views.properties.tabbed.AbstractPropertySection抽象類的類,用于描述這個section的控件和布局。
    ???4)aftersection和上面的aftertab差不多,描述的是同一個tab中的section的順序,注意afterserction描述的是本section之前的section的id
    ???5)filter:一個實現org.eclipse.jface.viewers.IFilter接口的過濾器,對選中元素進行過濾。
    ???6)enableFor:一個用于只是選擇數目的值,必須要符合這個舒服才能使能這個section。如果不符合,則這個section就被過濾了,如果省略本值,則section的使能器就不會工作了。這是一個自然數,比如,當enableFor=1時,僅僅只有一個元素被選擇的時候,本section才會被使能。

    some notes:
    ??? 上面說過實現ITabbedPropertySheetPageContributor接口的workbench part除了要實現getContributeId方法外,還需要重載getAdapter方法,因為eclipse的默認加載的property veiw時原來的那個view,為了使tabbed property view能夠加載,我們就需要重載getAdapter方法,返回一個TabbedPropertySheetPage對象。

    ??? 在實現section class的時候需要注意,createcontrol時首先應該先創建一個composite,一般是 Composite composite = getWidgetFactory().createFlatFormComposite(parent); 然后各個控件在這個composite上創建。


    posted @ 2006-09-17 22:24 Long Long Ago 閱讀(2879) | 評論 (1)編輯 收藏

    安裝subversion
    基本命令:
    ??$?sudo?apt-get?install?subversion
    ??$?sudo?apt-get?install?libapache2-svn
    可以安裝的包:
    ?apache2
    ?apache2-common
    ?apache2-mpm-prefork
    ?apache2-utils
    ?libapache2-svn
    ?libapache2-mod-auth-pam
    ?libapache2-mod-auth-sys-group
    ?subversion
    ?subversion-tools


    創建一個名為subversion的組:groupadd subversion
    將自己(eg.:user)和www-data(apapch2帳號)用戶添加入subversion組,可以編輯/etc/group文件,在最后找到subversion添加入帳號名(eg:user,www-data),看上去就像這樣:subversion:x:1001:www-data,exp
    然后是創建subversion庫,并賦予subversion組中用戶有讀寫subversion庫的權限:
    ???$?sudo?mkdir?/home/svn??#創建svn庫的父路徑
    ???$?cd?/home/svn
    ???$?sudo?mkdir?myproject??#創建本svn庫的目錄
    ???$?sudo?svnadmin?create?/home/svn/myproject?#使用svn命令,創建svn庫
    ???$?sudo?chown?-R?root:subversion?myproject?#更改本目錄的組
    ???$?sudo?chmod?-R?g+rws?myproject?#給本目錄的組用戶增加讀寫和遞歸增加新加目錄的讀寫權限
    注意上面提到的命令順序,如果最后再執行創建庫的命令(svnadmin create ....)則創建的文件沒有獲得組用戶寫的權限,這樣在外部訪問提交的時候會出錯.
    對于本機,可以直接使用file命令來訪問:
    ??$?svn?co(or?checkout)?file:///home/svn/myproject
    #or
    ??$?svn?co?file://localhost/home/svn/myproject
    注意:如果您并不確定主機的名稱,您必須使用三個斜杠(///),而如果您指定了主機的名稱,則您必須使用兩個斜杠(//).
    此時對svn庫的權限是基于文件系統的,只要是subversion組中的用戶都可以訪問本svn庫。

    接下來,講述如何使用apache服務器來提供對svn庫的訪問
    編輯文件/etc/apache2/mods-available/dav_svn.conf
    增加如下的內容:
    ??<Location?/svn/myproject>
    ?????DAV?svn
    ?????SVNPath?/home/svn/myproject
    ?????AuthType?Basic
    ?????AuthName?"myproject?subversion?repository"
    ?????AuthUserFile?/etc/subversion/passwd
    ?????
    <LimitExcept?GET?PROPFIND?OPTIONS?REPORT>
    ????????Require?valid-user
    ?????
    </LimitExcept>
    ??
    </Location>

    apache會解析url中的/svn/myproject部分,來定位svn庫,當收到此請求時,會查詢svn庫:/home/svn/myproject,這里的認證方式是basic,對于訪問要求valid-user,帳號文件在/etc/subversion/passwd中。
    注意重新設置后要重啟apache2:sudo /etc/init.d/apache2 restart
    編輯生成帳號文件: sudo htpasswd2 -c /etc/subversion/passwd user? #給user帳號創建口令
    這時候可以通過瀏覽器來瀏覽svn庫了
    在我的設置中發現,apache2會自動綁定ipv6地址,可能會有些問題,可以強制apache綁定v4地址,在/etc/apache2/port.conf中改成:Listen [bindedip]:[port]的形式

    通過https來訪問svn庫
    首先生成一個 SSL 簽名,使用命令

    ?# apache2-ssl-certificate

    這里會有一系列關于你的個人隱私的問題,回答完了,自然的簽名也就生成了,然
    后我們就要在 apache2 里面打開 SSL 了,現在要做的是開啟 ssl 模塊

    ?# a2enmod ssl

    然后,使用 apache2 的虛擬主機功能來添加 SSL 的支持,將

    ?/etc/apache2/sites-available/default

    復制一份,叫

    ?/etc/apache2/sites-available/ssl

    好啦

    修改 default 文件的開頭為

    ?NameVirtualHost *:80
    ?<VirtualHost *:80>

    修改 ssl 文件的開頭為

    ?NameVirtualHost *:443
    ?<VirtualHost *:443>

    這里 443 是 SSL 的標準端口。

    并在 ssl 文件中加入如下內容,在<VirtualHost></VirtualHost>內

    ?SSLEngine On
    ?SSLCertificateFile /etc/apache2/ssl/apache.pem

    保存文件后,運行命令

    ?? # a2ensite ssl

    來激活這個虛擬主機

    現在,修改文件

    ?/etc/apache2/ports.conf

    加上一行

    ?Listen 443

    好了,到此為止,SSL 服務器配置完成,重新啟動 apache 吧。

    ?

    一些問題:
    可能出現 RA layer request failed svn: MKACTIVITY of 400 Bad Request 之類的錯誤,這可能是因為使用了代理的原因,代理不支持svn的擴展命令,see:http://subversion.tigris.org/faq.html#proxy
    還有種原因,就是可能是你的客戶端使用的是windowsxp,其他版本的windows我沒試過,也是這樣的錯誤,在linux下正常,解決方法不太清楚。
    RA layer request failed svn: MKACTIVITY of 400 Bad Request,無論什么原因都可以用https代替http來暫時解決這樣的問題。

    參考:
    http://fanqiang.chinaunix.net/app/web/2005-05-18/3257.shtml
    http://wiki.ubuntu.org.cn/SubVersion?highlight=%28subversion%29

    posted @ 2006-09-05 17:00 Long Long Ago 閱讀(2985) | 評論 (0)編輯 收藏

    在sources.list中添加如下幾個源:
    deb?http://www.beerorkid.com/compiz/?dapper?main
    deb?http://xgl.compiz.info/?dapper?main
    deb-src?http://xgl.compiz.info/?dapper?main
    添加代理:
    export?http_proxy="http://xxx.xxx.xxx.xxx:xxxx"
    獲取pgp密鑰:
    wget?http://www.beerorkid.com/compiz/quinn.key.asc?-O?-?|?sudo?apt-key?add?-?

    nivida的驅動:
    sudo?apt-get?install?nvidia-kernel-common?nvidia-glx
    編輯文件:/etc/X11/xorg.conf
    在module部分中確定lode xgl,有如下代碼:
    Load?"glx"
    在devices部分修改除了Identifier行的其他各行,修改后如下:
    Section?"Device"
    ????Identifier-?leave?this?line?alone!
    ????Driver????????"nvidia"
    ????BusID????????"PCI:1:0:0"
    ????Option?????????"RenderAccel"?????????"true"
    EndSection
    在最下面添加Extensions部分,代碼如下:
    Section?"Extensions"
    ??????????Option??"Composite"?"Enable"
    EndSection
    下面是安裝必要的庫文件:
    sudo?apt-get?install?compiz?xserver-xgl?libgl1-mesa?xserver-xorg?libglitz-glx1?compiz-gnome
    以上是引文http://www.ubuntuforums.org/showthread.php?t=131267 中的方法,此文所講的后面是加載方法,我沒有采用,用的是這里講的方法:http://forum.ubuntu.org.cn/viewtopic.php?t=16777 不過這里講的安裝方法中少了一個庫文件,呵呵
    設置xgl啟動入口:
    新建一個xgl啟動腳本/usr/bin/startxgl.sh,內容如下:
    Xgl?-fullscreen?:1?-ac?-accel?glx:pbuffer?-accel?xv:pbuffer?&?sleep?2?&&?DISPLAY=:1
    #?Start?GNOME
    exec?gnome-session?
    使腳本可執行: sudo chmod 755 /usr/bin/startxgl.sh
    新建一個compiz腳本/usr/bin/startcompiz,內容如下:
    #!/bin/sh
    killall?gnome-window-decorator
    wait
    gnome-window-decorator?&?LD_PRELOAD=/usr/lib/fglrx/libGL.so.1.2.xlibmesa
    compiz?--replace?gconf?miniwin?decoration?transset?wobbly?fade?minimize?cube?rotate?zoom?scale?move?resize?place?switcher?trailfocus?water?&?
    使得腳本可執行:sudo chmod 755 /usr/bin/startcompiz
    在登陸管理器里建一個XGL會話: 建立一個文件/usr/share/xsessions/xgl.desktop ,內容如下:
    [Desktop?Entry]
    Encoding=UTF-8
    Name=XGl
    Exec=/usr/bin/startxgl.sh
    Icon=
    Type=Application?

    打開桌面菜單-〉系統-〉首選項-〉會話
    在最右邊的“啟動程序”里添加 /usr/bin/startcompiz 這句話
    最后不要忘了

    sudo aptitude update
    sudo aptitude upgrade
    關閉所有程序
    ctrl-alt-backspace啟動X
    登錄時在會話中選擇xgl
    會提示是否為默認會話,建議選擇僅本次
    哦,差點忘了,怎么使用:
    CTRL + ALT + Left/right arrow key. Switches to the new side of the cube for me.

    CTRL + ALT + SHIFT + Left/Right arrow key- Takes the in focused app around cube.

    CTRL + ALT + Left Click on Desktop - allows you to use the mouse to rotate cube.

    F12 - uses the Expose like trick

    Alt- Tab - switcher Vista-style
    看起來有點暈,尤其是輸入法的浮動窗體
    posted @ 2006-08-31 13:03 Long Long Ago 閱讀(910) | 評論 (3)編輯 收藏

    java.lang.NoClassDefFoundError: com/sun/tools/javac/Main
    最近在使用java的動態編譯的時候出現的問題,主要是由于在使用類com.sun.tool.javac.Main時,總是出現NoClassDefFoundError的錯誤,后來找到如下的文章,分析,可能是由于對于包tools.jar的加載問題,雖然我在classpath中聲明了這個包,但在eclipse環境下,始終都還是出現運行時異常,對于編譯時正確,運行時異常的情況,eclipse一般都是由于其自身的加載機制造成的.在eclipse下,對于一般的java工程,只要設置了系統的classpath,在其中添加了tools.jar包,即可;對于plugin工程,我是將tools.jar包,直接拷貝到本工程下,并在property中引用,而且在META-INF/MANIFEST.MF文件中的Runtime頁的classpath中添加了這個tool.jar包,這樣在運行時就沒有異常了,可以正常編譯了.

    理解Java?ClassLoader機制

    Java?ClassLoader

    2006-5-23
    當JVM(Java虛擬機)啟動時,會形成由三個類加載器組成的初始類加載器層次結構:

    ? ? ? ?bootstrap classloader
    ? ? ? ? ? ? ? ? |
    ? ? ? ?extension classloader
    ? ? ? ? ? ? ? ? |
    ? ? ? ?system classloader

    bootstrap classloader -引導(也稱為原始)類加載器,它負責加載Java的核心類。在Sun的JVM中,在執行java的命令中使用-Xbootclasspath選項或使用- D選項指定sun.boot.class.path系統屬性值可以指定附加的類。這個加載器的是非常特殊的,它實際上不是 java.lang.ClassLoader的子類,而是由JVM自身實現的。大家可以通過執行以下代碼來獲得bootstrap classloader加載了那些核心類庫:
    ? ?URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
    ? ?for (int i = 0; i < urls.length; i++) {
    ? ? ?System.out.println(urls.toExternalform());
    ? ?}
    在我的計算機上的結果為:
    文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xercesImpl-2.0.0.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/rt.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/i18n.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/jsse.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/jce.jar
    文件:/C:/j2sdk1.4.1_01/jre/lib/charsets.jar
    文件:/C:/j2sdk1.4.1_01/jre/classes
    這時大家知道了為什么我們不需要在系統屬性CLASSPATH中指定這些類庫了吧,因為JVM在啟動的時候就自動加載它們了。

    extension classloader -擴展類加載器,它負責加載JRE的擴展目錄(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系統屬性指定的)中JAR的類包。這為引入除Java核心類以外的新功能提供了一個標準機制。因為默認的擴展目錄對所有從同一個JRE中啟動的JVM都是通用的,所以放入這個目錄的 JAR類包對所有的JVM和system classloader都是可見的。在這個實例上調用方法getParent()總是返回空值null,因為引導加載器bootstrap classloader不是一個真正的ClassLoader實例。所以當大家執行以下代碼時:
    ? ?System.out.println(System.getProperty("java.ext.dirs"));
    ? ?ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();
    ? ?System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());
    結果為:
    C:\j2sdk1.4.1_01\jre\lib\ext
    the parent of extension classloader : null
    extension classloader是system classloader的parent,而bootstrap classloader是extension classloader的parent,但它不是一個實際的classloader,所以為null。

    system classloader -系統(也稱為應用)類加載器,它負責在JVM被啟動時,加載來自在命令java中的-classpath或者java.class.path系統屬性或者 CLASSPATH操作系統屬性所指定的JAR類包和類路徑。總能通過靜態方法ClassLoader.getSystemClassLoader()找到該類加載器。如果沒有特別指定,則用戶自定義的任何類加載器都將該類加載器作為它的父加載器。執行以下代碼即可獲得:
    ? ?System.out.println(System.getProperty("java.class.path"));
    輸出結果則為用戶在系統屬性里面設置的CLASSPATH。
    classloader 加載類用的是全盤負責委托機制。所謂全盤負責,即是當一個classloader加載一個Class的時候,這個Class所依賴的和引用的所有 Class也由這個classloader負責載入,除非是顯式的使用另外一個classloader載入;委托機制則是先讓parent(父)類加載器 (而不是super,它與parent classloader類不是繼承關系)尋找,只有在parent找不到的時候才從自己的類路徑中去尋找。此外類加載還采用了cache機制,也就是如果 cache中保存了這個Class就直接返回它,如果沒有才從文件中讀取和轉換成Class,并存入cache,這就是為什么我們修改了Class但是必須重新啟動JVM才能生效的原因。


    每個ClassLoader加載Class的過程是:
    1.檢測此Class是否載入過(即在cache中是否有此Class),如果有到8,如果沒有到2
    2.如果parent classloader不存在(沒有parent,那parent一定是bootstrap classloader了),到4
    3.請求parent classloader載入,如果成功到8,不成功到5
    4.請求jvm從bootstrap classloader中載入,如果成功到8
    5.尋找Class文件(從與此classloader相關的類路徑中尋找)。如果找不到則到7.
    6.從文件中載入Class,到8.
    7.拋出ClassNotFoundException.
    8.返回Class.

    其中5.6步我們可以通過覆蓋ClassLoader的findClass方法來實現自己的載入策略。甚至覆蓋loadClass方法來實現自己的載入過程。

    類加載器的順序是:
    先是bootstrap classloader,然后是extension classloader,最后才是system classloader。大家會發現加載的Class越是重要的越在靠前面。這樣做的原因是出于安全性的考慮,試想如果system classloader“親自”加載了一個具有破壞性的“java.lang.System”類的后果吧。這種委托機制保證了用戶即使具有一個這樣的類,也把它加入到了類路徑中,但是它永遠不會被載入,因為這個類總是由bootstrap classloader來加載的。大家可以執行一下以下的代碼:
    ? ?System.out.println(System.class.getClassLoader());
    將會看到結果是null,這就表明java.lang.System是由bootstrap classloader加載的,因為bootstrap classloader不是一個真正的ClassLoader實例,而是由JVM實現的,正如前面已經說過的。

    下面就讓我們來看看JVM是如何來為我們來建立類加載器的結構的:
    sun.misc.Launcher,顧名思義,當你執行java命令的時候,JVM會先使用bootstrap classloader載入并初始化一個Launcher,執行下來代碼:
    ? System.out.println("the Launcher's classloader is "+sun.misc.Launcher.getLauncher().getClass().getClassLoader());
    結果為:
    ? the Launcher's classloader is null (因為是用bootstrap classloader加載,所以class loader為null)
    Launcher 會根據系統和命令設定初始化好class loader結構,JVM就用它來獲得extension classloader和system classloader,并載入所有的需要載入的Class,最后執行java命令指定的帶有靜態的main方法的Class。extension classloader實際上是sun.misc.Launcher$ExtClassLoader類的一個實例,system classloader實際上是sun.misc.Launcher$AppClassLoader類的一個實例。并且都是 java.net.URLClassLoader的子類。

    讓我們來看看Launcher初試化的過程的部分代碼。

    Launcher的部分代碼:
    public class Launcher ?{
    ? ?public Launcher() {
    ? ? ? ?ExtClassLoader extclassloader;
    ? ? ? ?try {
    ? ? ? ? ? ?//初始化extension classloader
    ? ? ? ? ? ?extclassloader = ExtClassLoader.getExtClassLoader();
    ? ? ? ?} catch(IOException ioexception) {
    ? ? ? ? ? ?throw new InternalError("Could not create extension class loader");
    ? ? ? ?}
    ? ? ? ?try {
    ? ? ? ? ? ?//初始化system classloader,parent是extension classloader
    ? ? ? ? ? ?loader = AppClassLoader.getAppClassLoader(extclassloader);
    ? ? ? ?} catch(IOException ioexception1) {
    ? ? ? ? ? ?throw new InternalError("Could not create application class loader");
    ? ? ? ?}
    ? ? ? ?//將system classloader設置成當前線程的context classloader(將在后面加以介紹)
    ? ? ? ?Thread.currentThread().setContextClassLoader(loader);
    ? ? ? ?......
    ? ?}
    ? ?public ClassLoader getClassLoader() {
    ? ? ? ?//返回system classloader
    ? ? ? ?return loader;
    ? ?}
    }

    extension classloader的部分代碼:
    static class Launcher$ExtClassLoader extends URLClassLoader {

    ? ?public static Launcher$ExtClassLoader getExtClassLoader()
    ? ? ? ?throws IOException
    ? ?{
    ? ? ? ?File afile[] = getExtDirs();
    ? ? ? ?return (Launcher$ExtClassLoader)AccessController.doPrivileged(new Launcher$1(afile));
    ? ?}
    ? private static File[] getExtDirs() {
    ? ? ? ?//獲得系統屬性“java.ext.dirs”
    ? ? ? ?String s = System.getProperty("java.ext.dirs");
    ? ? ? ?File afile[];
    ? ? ? ?if(s != null) {
    ? ? ? ? ? ?StringTokenizer stringtokenizer = new StringTokenizer(s, File.pathSeparator);
    ? ? ? ? ? ?int i = stringtokenizer.countTokens();
    ? ? ? ? ? ?afile = new File;
    ? ? ? ? ? ?for(int j = 0; j < i; j++)
    ? ? ? ? ? ? ? ?afile[j] = new File(stringtokenizer.nextToken());

    ? ? ? ?} else {
    ? ? ? ? ? ?afile = new File[0];
    ? ? ? ?}
    ? ? ? ?return afile;
    ? ?}
    }

    system classloader的部分代碼:
    static class Launcher$AppClassLoader extends URLClassLoader
    {

    ? ?public static ClassLoader getAppClassLoader(ClassLoader classloader)
    ? ? ? ?throws IOException
    ? ?{
    ? ? ? ?//獲得系統屬性“java.class.path”
    ? ? ? ?String s = System.getProperty("java.class.path");
    ? ? ? ?File afile[] = s != null ? Launcher.access$200(s) : new File[0];
    ? ? ? ?return (Launcher$AppClassLoader)AccessController.doPrivileged(new Launcher$2(s, afile, classloader));
    ? ?}
    }

    看了源代碼大家就清楚了吧,extension classloader是使用系統屬性“java.ext.dirs”設置類搜索路徑的,并且沒有parent。system classloader是使用系統屬性“java.class.path”設置類搜索路徑的,并且有一個parent classloader。Launcher初始化extension classloader,system classloader,并將system classloader設置成為context classloader,但是僅僅返回system classloader給JVM。

      這里怎么又出來一個context classloader呢?它有什么用呢?我們在建立一個線程Thread的時候,可以為這個線程通過setContextClassLoader方法來指定一個合適的classloader作為這個線程的context classloader,當此線程運行的時候,我們可以通過getContextClassLoader方法來獲得此context classloader,就可以用它來載入我們所需要的Class。默認的是system classloader。利用這個特性,我們可以“打破”classloader委托機制了,父classloader可以獲得當前線程的context classloader,而這個context classloader可以是它的子classloader或者其他的classloader,那么父classloader就可以從其獲得所需的 Class,這就打破了只能向父classloader請求的限制了。這個機制可以滿足當我們的classpath是在運行時才確定,并由定制的 classloader加載的時候,由system classloader(即在jvm classpath中)加載的class可以通過context classloader獲得定制的classloader并加載入特定的class(通常是抽象類和接口,定制的classloader中是其實現),例如web應用中的servlet就是用這種機制加載的.


    好了,現在我們了解了classloader的結構和工作原理,那么我們如何實現在運行時的動態載入和更新呢?只要我們能夠動態改變類搜索路徑和清除classloader的cache中已經載入的Class就行了,有兩個方案,一是我們繼承一個classloader,覆蓋loadclass方法,動態的尋找Class文件并使用defineClass方法來;另一個則非常簡單實用,只要重新使用一個新的類搜索路徑來new一個classloader就行了,這樣即更新了類搜索路徑以便來載入新的Class,也重新生成了一個空白的cache(當然,類搜索路徑不一定必須更改)。噢,太好了,我們幾乎不用做什么工作,java.netURLClassLoader正是一個符合我們要求的classloader!我們可以直接使用或者繼承它就可以了!

    這是j2se1.4 API的doc中URLClassLoader的兩個構造器的描述:
    URLClassLoader(URL[] urls)
    ? ? ? ? ?Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader.
    URLClassLoader(URL[] urls, ClassLoader parent)
    ? ? ? ? ?Constructs a new URLClassLoader for the given URLs.
    其中URL[] urls就是我們要設置的類搜索路徑,parent就是這個classloader的parent classloader,默認的是system classloader。


    好,現在我們能夠動態的載入Class了,這樣我們就可以利用newInstance方法來獲得一個Object。但我們如何將此Object造型呢?可以將此Object造型成它本身的Class嗎?

    首先讓我們來分析一下java源文件的編譯,運行吧!javac命令是調用“JAVA_HOME/lib/tools.jar”中的“com.sun.tools.javac.Main”的compile方法來編譯:

    ? ?public static int compile(String as[]);

    ? ?public static int compile(String as[], PrintWriter printwriter);

    返回0表示編譯成功,字符串數組as則是我們用javac命令編譯時的參數,以空格劃分。例如:
    javac -classpath c:\foo\bar.jar;. -d c:\ c:\Some.java
    則字符串數組as為{"-classpath","c:\\foo\\bar.jar;.","-d","c:\\","c:\\Some.java"},如果帶有PrintWriter參數,則會把編譯信息出到這個指定的printWriter中。默認的輸出是System.err。

    其中 Main是由JVM使用Launcher初始化的system classloader載入的,根據全盤負責原則,編譯器在解析這個java源文件時所發現的它所依賴和引用的所有Class也將由system classloader載入,如果system classloader不能載入某個Class時,編譯器將拋出一個“cannot resolve symbol”錯誤。

    所以首先編譯就通不過,也就是編譯器無法編譯一個引用了不在CLASSPATH中的未知Class的java源文件,而由于拼寫錯誤或者沒有把所需類庫放到CLASSPATH中,大家一定經常看到這個“cannot resolve symbol”這個編譯錯誤吧!

    其次,就是我們把這個Class放到編譯路徑中,成功的進行了編譯,然后在運行的時候不把它放入到CLASSPATH中而利用我們自己的 classloader來動態載入這個Class,這時候也會出現“java.lang.NoClassDefFoundError”的違例,為什么呢?

    我們再來分析一下,首先調用這個造型語句的可執行的Class一定是由JVM使用Launcher初始化的system classloader載入的,根據全盤負責原則,當我們進行造型的時候,JVM也會使用system classloader來嘗試載入這個Class來對實例進行造型,自然在system classloader尋找不到這個Class時就會拋出“java.lang.NoClassDefFoundError”的違例。

    OK,現在讓我們來總結一下,java文件的編譯和Class的載入執行,都是使用Launcher初始化的system classloader作為類載入器的,我們無法動態的改變system classloader,更無法讓JVM使用我們自己的classloader來替換system classloader,根據全盤負責原則,就限制了編譯和運行時,我們無法直接顯式的使用一個system classloader尋找不到的Class,即我們只能使用Java核心類庫,擴展類庫和CLASSPATH中的類庫中的Class。

    還不死心!再嘗試一下這種情況,我們把這個Class也放入到CLASSPATH中,讓system classloader能夠識別和載入。然后我們通過自己的classloader來從指定的class文件中載入這個Class(不能夠委托 parent載入,因為這樣會被system classloader從CLASSPATH中將其載入),然后實例化一個Object,并造型成這個Class,這樣JVM也識別這個Class(因為 system classloader能夠定位和載入這個Class從CLASSPATH中),載入的也不是CLASSPATH中的這個Class,而是從 CLASSPATH外動態載入的,這樣總行了吧!十分不幸的是,這時會出現“java.lang.ClassCastException”違例。

    為什么呢?我們也來分析一下,不錯,我們雖然從CLASSPATH外使用我們自己的classloader動態載入了這個Class,但將它的實例造型的時候是JVM會使用system classloader來再次載入這個Class,并嘗試將使用我們的自己的classloader載入的Class的一個實例造型為system classloader載入的這個Class(另外的一個)。大家發現什么問題了嗎?也就是我們嘗試將從一個classloader載入的Class的一個實例造型為另外一個classloader載入的Class,雖然這兩個Class的名字一樣,甚至是從同一個class文件中載入。但不幸的是JVM 卻認為這個兩個Class是不同的,即JVM認為不同的classloader載入的相同的名字的Class(即使是從同一個class文件中載入的)是不同的!這樣做的原因我想大概也是主要出于安全性考慮,這樣就保證所有的核心Java類都是system classloader載入的,我們無法用自己的classloader載入的相同名字的Class的實例來替換它們的實例。

    看到這里,聰明的讀者一定想到了該如何動態載入我們的Class,實例化,造型并調用了吧!

    那就是利用面向對象的基本特性之一的多形性。我們把我們動態載入的Class的實例造型成它的一個system classloader所能識別的父類就行了!這是為什么呢?我們還是要再來分析一次。當我們用我們自己的classloader來動態載入這我們只要把這個Class的時候,發現它有一個父類Class,在載入它之前JVM先會載入這個父類Class,這個父類Class是system classloader所能識別的,根據委托機制,它將由system classloader載入,然后我們的classloader再載入這個Class,創建一個實例,造型為這個父類Class,注意了,造型成這個父類 Class的時候(也就是上溯)是面向對象的java語言所允許的并且JVM也支持的,JVM就使用system classloader再次載入這個父類Class,然后將此實例造型為這個父類Class。大家可以從這個過程發現這個父類Class都是由 system classloader載入的,也就是同一個class loader載入的同一個Class,所以造型的時候不會出現任何異常。而根據多形性,調用這個父類的方法時,真正執行的是這個Class(非父類 Class)的覆蓋了父類方法的方法。這些方法中也可以引用system classloader不能識別的Class,因為根據全盤負責原則,只要載入這個Class的classloader即我們自己定義的 classloader能夠定位和載入這些Class就行了。

    這樣我們就可以事先定義好一組接口或者基類并放入CLASSPATH中,然后在執行的時候動態的載入實現或者繼承了這些接口或基類的子類。還不明白嗎?讓我們來想一想Servlet吧,web application server能夠載入任何繼承了Servlet的Class并正確的執行它們,不管它實際的Class是什么,就是都把它們實例化成為一個Servlet Class,然后執行Servlet的init,doPost,doGet和destroy等方法的,而不管這個Servlet是從web- inf/lib和web-inf/classes下由system classloader的子classloader(即定制的classloader)動態載入。說了這么多希望大家都明白了。在applet,ejb等容器中,都是采用了這種機制.

    對于以上各種情況,希望大家實際編寫一些example來實驗一下。

    最后我再說點別的, classloader雖然稱為類加載器,但并不意味著只能用來加載Class,我們還可以利用它也獲得圖片,音頻文件等資源的URL,當然,這些資源必須在CLASSPATH中的jar類庫中或目錄下。我們來看API的doc中關于ClassLoader的兩個尋找資源和Class的方法描述吧:
            public URL getResource(String name)
            用指定的名字來查找資源,一個資源是一些能夠被class代碼訪問的在某種程度上依賴于代碼位置的數據(圖片,音頻,文本等等)。
    ? ? ? ? ? ? ? ?一個資源的名字是以'/'號分隔確定資源的路徑名的。
    ? ? ? ? ? ? ? ?這個方法將先請求parent classloader搜索資源,如果沒有parent,則會在內置在虛擬機中的classloader(即bootstrap classloader)的路徑中搜索。如果失敗,這個方法將調用findResource(String)來尋找資源。
            public static URL getSystemResource(String name)
    ? ? ? ? ? ? ? ?從用來載入類的搜索路徑中查找一個指定名字的資源。這個方法使用system class loader來定位資源。即相當于ClassLoader.getSystemClassLoader().getResource(name)。

    例如:
    ? ?System.out.println(ClassLoader.getSystemResource("java/lang/String.class"));
    的結果為:
    ? ?jar:文件:/C:/j2sdk1.4.1_01/jre/lib/rt.jar!/java/lang/String.class
    表明String.class文件在rt.jar的java/lang目錄中。
    因此我們可以將圖片等資源隨同Class一同打包到jar類庫中(當然,也可單獨打包這些資源)并添加它們到class loader的搜索路徑中,我們就可以無需關心這些資源的具體位置,讓class loader來幫我們尋找了!
    posted @ 2006-08-20 17:28 Long Long Ago 閱讀(12588) | 評論 (5)編輯 收藏

    最近做的gef編輯器在刪除時遇到了一些問題,就是不能通過delete鍵刪除,到處搜集資料,解決了,
    首先需要在相應rcp工程中的ActionBarAdviser類中注冊相應的Action,比如對應于deleteAction,我在方法org.eclipse.ui.application.ActionBarAdvisor#makeAction(IWorkbenchWindow)中注冊deleteAction,如下:
    protected?void?makeAction(final?IWorkbenchWindow?window){
    ????IAction?delAction?
    =?ActionFactory.DELETE.create(window);
    ????register(delAction);
    }


    只是這么設置還是不能刪除相應的圖形元素,需要在相應的編輯器中重載init方法,添加如下的代碼
    public?void?init(IEditorSite?site,?IEditorInput?input)?throws?PartInitException?{
    ????
    //?TODO?Auto-generated?method?stub
    ????super.init(site,?input);
    ????ActionRegistry?registry?
    =?getActionRegistry();
    ????IActionBars?bar?
    =?site.getActionBars();
    ????String?id?
    =?ActionFactory.DELETE.getId();
    ????bar.setGlobalActionHandler(id,registry.getAction(id));
    ????bar.updateActionBars();
    }

    在這里仔細研究會發現,在第一段代碼中實際上時創建了一個action,這是一個RetargetAction,而在super.init()方法會調用一個createAction方法,這里創建的是gef默認的redoAction?undoAction selectionAction deleteAction saveAction.需要注意的是RetargetAction是一種可以跟蹤當前活動的部分,由于retargetAction含有一個id,如果這個活動部分提供的handler的id和retargetAction的id相同,那么相應的對retargetAction的調用就轉嫁成對當前這個活動部分的handle的調用,(當然如果根本就沒有handle,那么這個action會disable).所以,我們可以看出來,這個retargetAction會在gef編輯器激活后調用gef的deleteAction.
    posted @ 2006-08-19 17:35 Long Long Ago 閱讀(1197) | 評論 (0)編輯 收藏

    http://www.approc.com/

    [Java|Eclipse|GEF]Figure是如何定位和設置大小?

    有兩種會創建圖元:
    1、打開圖形文件時,先會創建所有的model,這樣圖元的定位和設置大小是依據圖元model的size和location屬性,通過圖元EditPart.refreshVisuals()來設置的
    2、通過拖放托盤里面的圖標來建立的圖元,會先設置model的size,然后通過model對應的figure.setPreferredSize()來設置了,而location則是通過鼠標的位置來確定的
    posted @ 2006-08-14 11:18 Long Long Ago 閱讀(678) | 評論 (0)編輯 收藏

    http://www.approc.com/
    現在在3.1里面訪問資源文件變得比較簡單了,用戶可以通過繼承osgi.util.NLS,典型例子:
    public?class?MsgBoundle?extends?NLS{
    ??????
    private?final?static?String?BOUNDLE_NAME?
    ???????????????????????
    =?"resource.msg";
    ??????
    public?static?String?mo_1;
    ??????
    public?static?String?mo_2;

    ??????
    static{
    ?????????NLS.initializeMessage(BOUNDLE_NAME,
    ???????????????????????MsgBoundle.
    class);
    ??????}

    }


    好啦,現在就可以建立資源文件了,文件應該建在哪里呢?跟蹤代碼發現,這個由BOUNDLE_NAME決定,在MessageResourceBoundle.buildVariants()中,會將BOUNDLE_NAME中的“."替換成"/",然后再根據地區設定組合幾種不同的資源文件名稱,比如我的就是:

    1、resource/msg_zh_CN.properties
    2、resource/msg_zh.properties
    3、resource/msg.properties
    注意:這三個文件是有順序的哦

    然后通過EclipseClassLoader.getResource()來查找這些文件,并將文件中的值賦予給MsgBoundle對應的成員變量。
    posted @ 2006-08-14 11:16 Long Long Ago 閱讀(837) | 評論 (0)編輯 收藏

    主站蜘蛛池模板: 毛片免费视频观看| 亚洲欧洲国产成人综合在线观看| 亚洲jizzjizz在线播放久| 四虎成人免费网址在线| 国产精品亚洲专区一区| 国产成人亚洲精品青草天美 | 国产一级淫片a免费播放口| 久久夜色精品国产噜噜噜亚洲AV | 在线播放免费人成毛片乱码 | 成人无码a级毛片免费| 亚洲成人免费网站| 免费国产在线观看| 久久精品成人免费网站| 亚洲日韩看片无码电影| 亚洲小说区图片区另类春色| 最近2022中文字幕免费视频| 真正全免费视频a毛片| 久久亚洲精精品中文字幕| 国产婷婷高清在线观看免费| 日韩免费高清大片在线| 亚洲6080yy久久无码产自国产| 国产亚洲婷婷香蕉久久精品| 免费观看一级毛片| 污视频在线免费观看| 青青青亚洲精品国产| 亚洲精品福利在线观看| 亚洲日韩在线观看免费视频| 美女视频黄是免费的网址| 99麻豆久久久国产精品免费| 亚洲Aⅴ在线无码播放毛片一线天| 亚洲网红精品大秀在线观看| 亚洲国产婷婷香蕉久久久久久| 久久这里只有精品国产免费10| 久艹视频在线免费观看| 青青青视频免费观看| 亚洲欧洲无码一区二区三区| 久久亚洲sm情趣捆绑调教 | 亚洲韩国—中文字幕| 亚洲色精品88色婷婷七月丁香| 国产片免费在线观看| 久久不见久久见免费影院|