跟大家講解下有關通過 JXTA 進行無線通信,第 2 部分: 實現 JXTA,相信小伙伴們對這個話題應該也很關注吧,現在就為小伙伴們說說通過 JXTA 進行無線通信,第 2 部分: 實現 JXTA,小編也收集到了有關通過 JXTA 進行無線通信,第 2 部分: 實現 JXTA的相關資料,希望大家看到了會喜歡。
在本系列的第一篇文章中,介紹了支持 Java 2 Platform, Micro Edition(J2ME)的設備為什么不能直接承載進行企業通信的客戶端應用程序。第一篇文章還討論了如何用 JXTA 技術在企業通信解決方案中集成瘦移動客戶機(thin mobile client),并展示了如何在 J2M
在本系列的第一篇文章中,介紹了支持 Java 2 Platform, Micro Edition(J2ME)的設備為什么不能直接承載進行企業通信的客戶端應用程序。第一篇文章還討論了如何用 JXTA 技術在企業通信解決方案中集成瘦移動客戶機(thin mobile client),并展示了如何在 J2ME 設備中使用 JXTA 協議。
本系列的第二篇文章將展示如何用 JXTA(JXTA-for-JMS,或簡稱 JXTA4JMS)實現 J2ME 客戶機與 JMS 應用程序之間的連接。該 JXTA4JMS 實現包含兩個組件:一個組件運行在臺式計算機上(桌面端 JXTA4JMS),另一個組件在基于 J2ME 的移動設備上運行(移動端 JXTA4JMS)。
本文首先將用幾個實例展示 J2ME 設備或者 JMS 客戶機如何使用 JXTA4JMS。然后介紹 JXTA4JMS 架構,并說明 JXTA4JMS 實現中的類的部署情況。最后,本文將展示實際的實現,并提供一個可以工作的應用程序,該程序在 Java Message Service(JMS)應用程序中集成了瘦 J2ME 客戶機。
考慮一家用 JMS 完成其企業范圍通信需求的公司。這家公司安裝了 JMS 提供者(一種消息中間件),使用 JMS 網絡的雇員就是通信的客戶機(消息的生產者或消費者)。公司希望通信客戶機彼此保持連接。當所有用戶可以訪問他們的 PC 時,JMS 網絡就可以滿足這項要求。
當用戶不能訪問他們的 PC 時(例如,當他們不在辦公室時),就需要使用 JXTA4JMS。假設 JMS 用戶 Alice 離開了辦公室。她啟用了桌面上和手機上的 JXTA4JMS。桌面端的 JXTA4JMS 開始監聽收到的 JMS 消息。每當收到 Alice 的 JMS 消息,它就將這個消息轉發給 Alice 的手機。與此類似,當 Alice 要向同事發送消息時,她使用移動端 JXTA4JMS 發送這條 JMS 消息。消息接收者像接收普通 JMS 消息那樣接收這條消息。JXTA4JMS 幫助 Alice 保持連接,并用支持 J2ME 的手機繼續通過 JMS 網絡收發消息。
注意 JXTA4JMS 的一項重要功能:它不干擾其他 JMS 用戶的工作。如果 Alice 不想再使用 JXTA4JMS,只要禁用它就行了。JXTA4JMS 對于其他 JMS 用戶來說是透明的,他們不受 JXTA4JMS 的影響,因此不會知道 Alice 是啟用還是禁用了 JXTA4JMS。
如您所見,JXTA4JMS 可以處理 JMS 客戶機和移動客戶之間的雙向通信。讓我們分別考慮這兩種用例,以便清楚地闡明 JXTA4JMS 的功能。
回頁首
從 JMS 客戶機到 J2ME 客戶機的通信
假定 Bob 要向 Alice 發送 JMS 消息。Bob 用他的桌面計算機發送這個消息。Bob 不知道 Alice 這個時候不在她的辦公室,也不知道她啟用了桌面計算機和 J2ME 手機上的 JXTA4JMS。
在 Alice 啟用桌面計算機和手機中的 JXTA4JMS 之后,這兩端都做好了進行消息交換的準備:
桌面端 JXTA4JMS 開始監聽 JMS 提供者上 Alice 隊列中的 JMS 消息。移動端 JXTA4JMS 創建一個輸入 JXTA 管道,并開始監聽傳入的消息。桌面端 JXTA4JMS 搜索這個管道,將它當成一個輸出管道,并用它向移動電話發送消息。因此,這個管道被用來從桌面向移動電話發送消息。注意,JXTA 管道是有兩個端口(入口和出口)的虛擬通信信道。創建這個管道的一方位于入口,搜索它的一方位于出口。需要另一個 JXTA 管道從手機向桌面發送消息。桌面端 JXTA4JMS 創建了這個管道,并開始監聽管道中來自移動端 JXTA4JMS 的消息。移動端 JXTA4JMS 搜索這個管道,并用它向桌面發送 JMS 消息。現在,有兩個 JXTA 管道:一個用于從桌面端發送到移動端的消息,另一個用于從移動端發送到桌面端的消息。
現在看看當 Bob(JMS 用戶)向 Alice(JXTA4JMS 用戶)發送消息時會發生什么。圖 1 以圖形方式顯示了一系列事件。
圖 1. 從 JMS 客戶機向 J2ME 客戶機發送消息
從 J2ME 客戶機到 JMS 客戶機的通信
現在,看看 JXTA4JMS 是如何幫助 Alice 向 Bob 發送回復消息的。圖 2 以圖形方式展示了這個場景。
圖 2. 從 J2ME 客戶機向 JMS 客戶機發送消息
圖 2 中的這個場景與圖 1 中的類似:
Alice 寫下回復給 Bob 的消息,并讓手機中運行的移動端 JXTA4JMS 將這條消息發送給 Bob。JXTA4JMS 將 Alice 的消息包裝為第一篇文章中討論過的 JXME 消息格式。這條 JXME 消息還包裝了 JMS 消息接收者的名字(在這里是 Bob)。JXTA4JMS 將消息發送給 JXTA 中繼器。注意,這條消息是通過 Alice 的桌面端 JXTA4JMS 創建的 JXTA 管道發送的。JXTA 中繼器將 JXME 格式轉換為 JXTA 格式。JXTA 中繼器通過 JXTA 網絡將 JXTA 消息發到 Alice 的桌面,同時,在 Alice 的桌面端,JXTA4JMS 已經啟用。桌面端 JXTA4JMS 接收到來自中繼器的消息。注意,消息現在是 JXTA 格式的。桌面端 JXTA4JMS 將消息轉換為 JMS 格式。它提取出接收 Alice 所發出消息的 JMS 客戶用戶名(在這里是 Bob),而且還將搜索 JMS 提供者上的 Bob 隊列。桌面端 JXTA4JMS 將消息發送到 JMS 提供者上的 Bob 隊列。Bob 的客戶端 JMS 應用程序監聽 Bob 的隊列,并接收到來自 Alice 的消息。回頁首
JXTA-for-JMS 架構
讓我們分析一下 JXTA4JMS 實現的架構。正如在 JXTA4JMS 使用模型的討論中已經看到的,JXTA4JMS 實現由桌面實現和移動端實現組成。現在來分析一下這兩個實現。
桌面端實現由多個層組成,如圖 3 所示。分層的架構可以確保 JXTA4JMS 有更好的重用性。您可以根據自已的需求很容易地修改 JXTA4JMS 的任何層,并在其他層保持不變的情況下使用它。
圖 3. 桌面端 JXTA4JMS 層
下面將介紹 JXTA4JMS 的每一層:
監聽器層監聽來自 JMS 網絡的 JMS 消息,以及來自 J2ME 設備的 JXTA 消息。監聽器層還利用 JMS 提供者建立 JMS 連接,并在 JXTA 網絡上創建管道,以便與 J2ME 移動設備進行通信。這一層只包含一個名為Listener 的類。路由器層擁有將 JMS 消息路由到 JMS 設備(通過 JXTA 網絡)的方法,以及將 J2ME 消息路由到 JMS 客戶機的方法。這一層由一個名為 Router 的類組成。Listener 監聽收到的消息,并用 Router 類正確路由消息。格式轉換層將 JMS 消息和 JXTA 格式相互轉換。這一層由兩個類組成:JXTAToJMSMsgConverter 和 JMSToJXTAMsgConverter。在將消息路由到目的地之前,Router 類用格式轉換類來轉換這些消息。網絡層處理使用 JMS 和 JXTA 網絡時的低級細節。這一層由類 JXTASearch 和 JMSSearch 組成。JXTASearch 類搜索由 J2ME 設備創建、并通過 JXTA 網絡發布的 JXTA 管道。可以用這個管道向 J2ME 設備發送消息。JMSSearch 類搜索 JMS 用戶(J2ME 客戶消息的接收者)的 JMS 隊列。Listener 和Router 類使用 JXTASearch 和 JMSSearch 這兩個類。JXTA4JMS 的移動端實現負責處理無線通信(從桌面端 JXTA4JMS 實現消息的接收和發送)。下面介紹的 4 個類可以在分層架構中工作,以便執行消息傳遞任務。
JXMEMIDlet 類是我編寫的一個 MIDlet 示例應用程序,用于展示如何使用一個有簡單用戶界面的 JXTA4JMS 通信客戶機。JXTA4JMSMessagingClient 類是 J2ME 端實現中最重要的類。JXTA4JMSMessagingClient 類提供所有低級功能,比如連接到中繼器,以及交換和處理消息。這使高層的應用程序可以只關注用戶界面內容。JXTA4JMSMessage 類生成 JXTA4JMS 消息,并對其進行處理。JXTA4JMS 消息是為這種應用程序定制的 JXME 消息。JXTA4JMSMessagingClient 使用這個類生成和處理 JXTA4JMS 消息。DataStore 類是低級工具類,它存儲用于接收來自 JMS 端實現的消息的管道標識符。DataStore 類處理使用 J2ME record store 的所有低級細節。JXTA4JMSMessagingClient 類使用這個工具類存儲和接收管道標識符。回頁首
配置 JXTA4JMS
在實現 JXTA4JMS 之前,應當首先考慮如何在 JXTA 網絡中配置 JXTA4JMS。這有助于了解 JXTA4JMS 如何使用 JXTA 集合點和中繼器。
JXTA4JMS 需要運行三個 JXTA 實例。第一個實例表示 JXTA 網絡上的 JMS 用戶(例如 Alice)。這個 JXTA 實例是 JXTA4JMS 實例的一部分。
第二個實例是一個集合點。集合點是所有 JXTA peer 聚會的地方。集合點將緩存 JXTA 廣告,使 JXTA peer 可以發現網絡上的其他 peer。我將在后面介紹集合點。
第三個實例是一個 JXTA 中繼器,移動 JXTA4JMS 用它在 JXTA 網絡上進行通信。
這三個 JXTA 實例的配置要求各有不同,我會在稍后說明。在真實的應用程序中,這三個實例運行在通過某種網絡技術(如 Internet)彼此連接的不同計算機上。
為了觀察可能沒有連接到任何網絡的單機中運行的 JXTA4JMS,可以讓這三個實例運行在同一臺計算機上。這個 JXTA4JMS 實現并不關心不同的實例是運行在同一計算機中,還是運行在不同的計算機中。您還可以配置同一個 JXTA 實例,讓它起到兩個或者三個實例的作用。
我將本文的源代碼打包在 wi-jxta2source.zip 文件中,在那里還可以找到一個 readme 文件,該文件解釋了我是如何測試 JXTA4JMS 的。
在第一次運行 JXTA 實例時,會看到 JXTA 配置窗口。對每一個實例,只需要填寫一次配置窗口中的字段即可。配置窗口由 basic、advanced、rendezvous/relays 和 security 選項卡組成。對于這三個 JXTA 實例,basic 和 security 選項卡中的項是相同的。圖 4 顯示了 basic 選項卡。
圖 4. JXTA 配置窗口中的 basic 選項卡
basic 選項卡有一個用于輸入 peer 名的文本框。例如,Alice 在配置在 JXTA 網絡上表示她的 JXTA 實例時,在這個文本框中填入字符串“Alice”。您配置集合點和 relay peer 時,可以使用任何名字。
basic 選項卡還包含一個名為“Use proxy server”的選擇框。只有當您位于防火墻后時,才需要選中這個選擇框。
security 選項卡包含輸入 JXTA 實例的登錄信息的字段。圖 5 顯示了包含 secure username 和 password 字段的 security 選項卡。
圖 5. JXTA 配置窗口里的 security 選項卡
secure username 與 basic 選項卡中的 peer name 字段不同。peer name 字段指定 JXTA 網絡中 peer 的名字,而 secure username 字段表示特定 JXTA 實例的登錄名。每次啟動 JXTA 實例時,都需要 secure username 和 password。
為了簡便起見,可以使用同一個名字(如 Alice)作為 peer name 和 secure username 字段的值。
現在讓我們看看如何配置 advanced 選項卡。advanced 選項卡有兩部分,一部分用于 TCP,一部分用于 HTTP 設置,如圖 6 所示。TCP 和 HTTP 設置包括這個 JXTA4JMS 實例監聽的 IP 地址和端口號。默認情況下,JXTA 的配置是對 HTTP 使用端口 9700,對 TCP 使用端口 9701。
圖 6. JXTA 配置窗口中的 advanced 選項卡
如果三個 JXTA 實例使用不同的計算機,那么只需要指定每臺計算機的 IP 地址即可。如果在同一計算機中運行多個 JXTA 實現,那么需要對每個 JXTA 實例使用不同的 TCP 和 HTTP 端口號。
例如,當我在一臺計算機中試驗這個應用程序時,我將 Alice 的 JXTA4JMS 實例的 HTTP 和 TCP 端口號分別配置為 9700 和 9701。我將第二個 JXTA 實例配置為既是集合點,又是 relay peer。第二個實例分別監聽 HTTP 和 TCP 的端口號 9702 和 9703。
在配置集合點和 relay peer 時,必須在 advanced 選項卡中再做一件或者幾件事:單擊 HTTP settings 部分中的 Enable Incoming Connections 選擇框。這使其他 peer 可以與集合點和 relay peer 建立 HTTP 連接。
如圖 7 所示的 rendezvous/relays 選項卡包含兩部分,一部分用于集合點,一部分用于 relay peer。
圖 7. JXTA 配置窗口中的 rendezvous/relays 選項卡
在配置 relay peer 時,必須選擇 rendezvous/relays 選項卡中的 Act as a JxtaProxy 和Act as a Relay 選擇框。在配置集合點時,必須選擇Act as a Rendezvous 選擇框。
還需要為其他兩個 JXTA 實例( JXTA4JMS 實例和中繼器實例)提供集合點實例的 IP 地址和端口號。為此,必須在 Available TCP rendezvous 和Available HTTP rendezvous 字段中填入集合點的 IP 地址和端口號。
現在對集合點做一簡單說明。真實的 JXTA 網絡包含許多集合點。如果 Bob 和 Alice 通過 JXTA 網絡進行通信,那么他們都至少要知道一個集合點。他們知道的不一定是同一個集合點。
如果 Bob 知道集合點 R1,而 Alice 知道集合點 R2,并且集合點 R1 和 R2 知道一個共同的集合點 R3,那么 Bob 和 Alice 就可以通過 JXTA 網絡進行無縫通信。JXTA 網絡自動管理 peer 的發現,Bob 和 Alice 不用關心這些細節。這是 JXTA 技術的一個重要特性。
現在讓我們討論桌面端 JXTA4JMS 實現的細節,然后再討論移動端的 JXTA4JMS 實現。
回頁首
實現桌面端 JXTA4JMS
我已經討論了 JXTA4JMS 架構中不同層的功能,因此我現在將展示這些層的實現。因為是上層使用低層,所以我對實現的討論是從下向上進行的。
JXTASearch 類
JXTASearch 類搜索 JXTA 網絡中的特定管道。JXTASearch 類以同步方式搜索管道,這意味著對JXTASearch 類的調用在找到管道之后才會返回。要搜索的管道是由移動端創建、發布和監聽的管道。這個管道將用于向 J2ME 設備發送消息。
JXTASearch 類包含一個構造函數和兩個方法:getPipeAdvertisement() 和 getOutputPipe()。此外,JXTASearch 類還實現了一個名為 OutputPipeListener 的接口。根據 JXTA 實現,任何想接收管道來創建通知的類都要實現OutputPipeListener 接口。OutputPipeListener 接口有一個名為outputPipeEvent() 的方法,它接收來自底層 JXTA 實現的管道解析通知(resolving notifiation)。很快您就會看到JXTASearch 類是如何實現outputPipeEvent() 方法的。
JXTASearch 構造函數
JXTASearch 構造函數(清單 1)采用了PeerGroup 類的一個實例。PeerGroup 類是與 JXTA peer 組交互的一個方便方式。它包含執行那些您可能想在 peer 組中執行的各種任務的方法。我會在后面描述如何使用這個類的兩個方法。現在,只需要注意JXTASearch 構造函數只是將 PeerGroup 對象存儲在類級的變量(名為 peerGroup),以便以后getPipeAdvertisement() 和getOutputPipe() 方法使用這些變量。
清單 1.JXTASearch 構造函數
public JXTASearch ( PeerGroup peerGroup ){ this.peerGroup = peerGroup; }getPipeAdvertisement() 方法
清單 2 中顯示的getPipeAdvertisement() 方法以管道名為參數,并搜索對應這個管道的廣告。Listener 類在調用getPipeAdvertisement() 方法時指定管道的名稱。
getPipeAdvertisement() 方法以 PipeAdvertisement 對象的形式返回管道的廣告。
清單 2. getPipeAdvertisement() 方法
public PipeAdvertisement getPipeAdvertisement ( String targetPipeName ) { Enumeration enum = null; PipeAdvertisement pipeAdvert = null; DiscoveryService discoveryService = peerGroup.getDiscoveryService (); try { while( true ){ discoveryService.getRemoteAdvertisements ( null, DiscoveryService.ADV,"Name", targetPipeName, 5 ); enum = discoveryService.getLocalAdvertisements ( DiscoveryService.ADV,"Name", targetPipeName ); if ( enum != null) { while (enum.hasMoreElements()) pipeAdvert = ( PipeAdvertisement ) enum.nextElement (); }//if(enum != null) if ( pipeAdvert != null ) break; }//while( true ) } catch ( Exception e ) { e.printStackTrace(); } return pipeAdvert; }//getPipeAdvertisement()getPipeAdvertisement() 方法中的第一項工作是調用存儲在構造函數中的 PeerGroup 對象的getDiscoveryService() 方法。這個getDiscoveryService() 方法調用將返回DiscoveryService 對象。
DiscoveryService 類表示一種稱為發現服務的 JXTA 服務。JXTA peer 利用發現服務搜索 JXTA 網絡上的不同資源。我使用 JXTA 發現服務搜索指定 peer 組中的特定管道。
如果想使用 JXTA 發現服務,那么需要編寫 XML 搜索查詢,并將查詢發送給前面配置 JXTA4JMS 中討論過的集合點。而且,必須處理搜索查詢找到的消息。
不過,JXTA 實現可以使您不用做這項工作。DiscoveryService 類很好地包裝了發現服務的功能。只需調用 DiscoveryService 類的幾個方法作出響應即可,不用擔心 JXTA 所要求和使用的 XML 語法。JXTA 實現負責這些底層細節。
獲得 DiscoveryService 對象后,可以用它的方法執行管道搜索操作。DiscoveryService 類有一個名為getRemoteAdvertisements() 的方法,它向 JXTA 網絡發送搜索查詢。getRemoteAdvertisements() 方法有 5 個參數:
第一個參數是一個標識符。如果知道要搜索的管道的標識符,那么可以將這個標識符作為第一個參數提供給 getRemoteAdvertisements() 方法,以加快遠程搜索。不過,在這里,我們不知道要搜索的管道的標識符。因此將 null 作為第一個參數傳遞給getRemoteAdvertisements() 方法調用。第二個參數指定要搜索的資源類型(例如,廣告、peer 或者 peer 組等)。我們搜索的是管道廣告,因此只需要使用 DiscoveryService 類的一個名為ADV 的靜態字段。如果搜索 peer 或者 peer 組,那么還要分別指定PEER 或者PEERGROUP 靜態字段。第三個參數是要搜索的屬性的名字。要搜索的是具有特定名字的管道廣告。因此,要搜索帶有 “Name” 屬性的特定值的管道。這類似于在第一篇文章的“JXME 編程”一節中討論的search() 方法的第二個參數。因此,我們將字符串“Name”作為第三個參數傳遞給getRemoteAdvertisements() 方法調用。第四個參數是要搜索的管道名(或者可以說 “Name” 屬性的值)。只需將管道的名字作為值傳遞即可。getRemoteAdvertisements() 方法的最后一個參數是要接收的搜索結果的最大數量。您不知道有多少 peer 會響應這個搜索查詢,以及每一個 peer 會返回多少個響應。因此,要對搜索操作進行限制。將“5”作為最后一個參數的值傳遞,這表示從每一個 peer 接收最多 5 個結果。getRemoteAdvertisements() 方法不返回任何東西。它將搜索結果存儲在由 JXTA 實現維護的本地緩沖中。因此,在調用getRemoteAdvertisements() 方法后,必須調用另一個getLocalAdvertisements() 方法以從本地緩沖區中提取搜索結果。
getLocalAdvertisements() 方法有三個參數:資源類型、屬性名和屬性值。這三個參數與 getRemoteAdvertisements() 方法的第二、三、四個參數是相同的。
getLocalAdvertisements() 方法返回一個 Enumeration 對象。Enumeration 對象包含以幾個Advertisement 對象的形式出現的搜索結果。
getLocalAdvertisement() 方法返回的 Enumeration 對象會有不同類型的 Advertisement 對象。因為搜索的是管道廣告,可以預計,如果 Enumeration 對象不是 null,那么它將包括一個或者多個PipeAdvertisement 對象。PipeAdvertisement 對象表示管道廣告。
讓我們遍歷整個 Enumeration 并取出最后的廣告,因為最后一個也是最新的。
getOutputPipe() 方法
清單 3 中所示的 getOutputPipe() 方法用一個 PipeAdvertisement 對象作為參數。通常,這個參數與在getPipeAdvertisement() 方法中搜索的PipeAdvertisement 對象是相同的。
getOutputPipe() 方法返回一個 OutputPipe 對象。這個 OutputPipe 對象表示管道廣告對應的 JXTA 管道。
清單 3. getOutputPipe() 方法
public OutputPipe getOutputPipe (PipeAdvertisement pipeAdvert){ try { PipeService pipeSvc = peerGroup.getPipeService (); pipeSvc.createOutputPipe ( pipeAdvert, this ); while ( true ) { if ( outputPipe != null ) break; }//while (true) return outputPipe; }//try catch ( Exception e ){ e.printStackTrace(); }//catch return null; }//getOutputPipe()正如需要使用 JXTA 發現服務來搜索管道廣告一樣,這里需要使用 JXTA 管道服務創建一個輸出管道。JXTA 實現提供了一個名為 PipeService 的類,它包裝了 JXTA 管道服務的功能。用PeerGroup 類的getPipeService() 方法可以得到PipeService 對象。
PipeService 類包含 createInputPipe() 和 createOutputPipe() 方法,可以用它們來創建進行消息交換的輸入和輸出管道。PipeService 類的createOutputPipe() 方法有兩個參數:PipeAdvertisement 和OutputPipeListener 對象。它用這個廣告創建一個 JXTA 管道,并返回一個OutputPipe 對象。
JXTA 管道創建過程是在 JXTA 網絡上完成的。我不會討論這些細節,不過 createOutputPipe() 方法不會等待管道創建好后才返回,所以它是異步返回的。
JXTA 網絡隨后以管道解析(resolving)事件的形式確認管道的創建。根據 JXTA 實現,只有實現 OutputPipeListener 接口的類才可以接收管道解析事件通知。OutputPipeListener 接口只包含一個名為outputPipeEvent() 的方法。JXTASearch 類實現了outputPipeEvent() 方法,因此它可以接收通知。
通知到達時,outputPipeEvent() 方法就獲得了控制權。outputPipeEvent() 實現很簡單,清單 4 顯示它只有一行。outputPipeEvent() 方法在名為outputPipe 的類級對象中存儲新創建的管道。
清單 4. outputPipeEvent() 方法 public void outputPipeEvent ( OutputPipeEvent e ) { outputPipe = e.getOutputPipe (); }//outputPipeEvent()為了簡化 JXTASearch 類的使用,我將 getOutputPipe() 方法封閉在一個無限 while 循環中,只有通知到達時,循環才會中斷。這個無限 while 循環不斷檢查outputPipeEvent() 方法是否設置了outputPipe 類級對象。當getOutputPipe() 方法發現所需要的OutputPipe 對象時,就會返回這個對象。
JXTAToJMSMsgConverter 類
JXTAToJMSMsgConverter 類使用 JXTA 消息,并將它轉換為相應的 JMS 消息。JXTAToJMSMsgConverter 類包含 4 個方法(一個構造函數和三個 getter 方法),這些方法的具體說明如下:
JXTAToJMSMsgConverter 構造函數
清單 5 顯示了 JXTAToJMSMsgConverter 構造函數。
清單 5. JXTAToJMSMsgConverter 構造函數public JXTAToJMSMsgConverter ( QueueConnectionFactory qConnFactory, net.jxta.endpoint.Message jxtaMsg ) throws JMSException { QueueConnection queueConnection = null; try { //Creating a new JMS text message. //Step 1: queueConnection = qConnFactory.createQueueConnection (); //Step 2: queueSession = QueueConnection.createQueueSession ( false, Session.AUTO_ACKNOWLEDGE ); //Step 3: jmsMessage = queueSession.createTextMessage (); } catch ( Exception e ) { e.printStackTrace (); } MessageElement jmsRecipientElement, msgElement; jmsRecipientElement = jxtaMsg.getMessageElement ("JMSRecipient"); msgElement = jxtaMsg.getMessageElement ("Message"); jmsRecipient = jmsRecipientElement.toString(); jmsMessage.setText (msgElement.toString()); }//JXTAToJMSMsgConverter這個構造函數有兩個參數,一個名為 qConnFactory 的 QueueConnectionFactory 對象,以及一個名為jxtaMsg 的net.jxta.endpoint.Message 對象。我將進一步分析這些對象。
Listener 類實例化了一個 QueueConnectionFactory 對象,并將這個對象作為 JXTAToJMSMsgConverter 構造函數的第一個參數進行傳遞。QueueConnectionFactory 是一個連接工廠和一個 JMS 管理的對象。根據 JMS 架構,需要一個QueueSession 對象來創建新的 JMS 消息。同時還需要一個隊列連接工廠(一個QueueConnectionFactory 對象)來創建QueueSession 對象。在文章的后面部分,可以看到如何用連接工廠創建 JMS 消息。
net.jxta.endpoint.Message 對象(JXTAToJMSMsgConverter 構造函數的第二個參數)是將轉換為 JMS 格式的 JXTA 消息。看一看JXTAToJMSMsgConverter 構造函數是如何工作的。
必須先創建一個新的 JMS 消息對象,這分為三步。首先,調用 QueueConnectionFactory 對象的 createQueueConnection() 方法。這會提供一個QueueConnection 對象。然后調用QueueConnection 對象的createQueueSession() 方法,它會給出一個QueueSession 對象。在第三步,調用QueueSession 對象的createTextMessage() 方法,它返回一個javax.jms.TextMessage 對象。這個javax.jms.TextMessage 對象名為jmsMessage。最后要用來自 JXTA 消息的數據填充這個 JMS 消息對象。注意清單 5 “創建一個新 JMS 文本消息”中的這三步。
在這里要注意的是,QueueSession 對象有創建不同類型的 JMS 消息的方法。例如,可以用 QueueSession 類的createByteMessage() 方法創建二進制形式的消息。不過,我選擇使用createTextMessage() 方法,因為我想在本系列文章中展示文本消息的交換。
現在看一下如何從收到的 JXTA 消息中提取數據,并用這些數據填充 JMS 消息。
收到的 JXTA 消息包含兩部分:消息接收者的名字和消息本身。JXTA 消息的每一部分在 JXTA 消息中都是一個元素。消息接收者的名字包裝在名為 “JMSRecipient” 的元素中。消息包裝在名為 “Message” 的元素中。
可以調用 net.jxta.endpoint.Message 類的 getMessageElement() 方法,并同時傳遞元素的名字。getMessageElement() 方法返回一個MessageElement 對象,它表示 JXTA 消息的一個元素。
我將在后面介紹 JXTA 消息中的每個元素的結構。現在,只需注意清單 5 中兩個MessageElement 對象即可,這兩個對象分別名為msgElement(為包裝消息的對象)和jmsRecipientElement(為包裝接收者名字的對象)。
現在可以調用每一個 MessageElement 對象的 toString() 方法。這個方法以字符串的形式返回MessageElement 的內容。
然后可以調用 jmsMessage 對象(即前面創建的 javax.jms.TextMessage 對象)的setText() 方法,同時傳遞 JXTA 消息內容。這會在jmsMessage 對象中設置 JXTA 消息內容。jmsMessage 對象現在就準備好了。
getQueueSession() 方法
getQueueSession() 方法(如清單 6 所示)返回在構造函數中創建的 QueueSession 對象。可以看到Router 類是如何調用getQueueSession() 方法來提取QueueSession 對象的。Router 類用這個QueueSession 對象將文本消息發送給 JMS 接收者。
清單 6. getQueueSession() 方法 public QueueSession getQueueSession() { return queueSession; }//getQueueSession()getMessage() 方法
getMessage() 方法(如清單 7 所示)返回在構造函數中創建并填充的 javax.jms.TextMessage 對象,該對象名為jmsMessage。
清單 7. getMessage() 方法 public javax.jms.TextMessage getMessage() { return jmsMessage; }//getMessage()getJMSRecepient() 方法
清單 8 中所見的 getJMSRecipient() 方法返回接收者的名字,這個名字可以從前面構造函數中傳入的 JXTA 消息中提取。
還要注意的是,在清單 8 中,已經將字符中 “jms/” 作為前綴串添加到 JMS 接收者的名字上。然后用接收者的名字作為 JMS 隊列名向隊列發送消息。“jms/” 字符串遵守了 Java 規范,所以 JMS 隊列的名稱是以“jms/” 字符串開頭的。
清單 8. getJMSRecipient() 方法 public String getJMSRecipient() { return"jms/"+ jmsRecipient; }//getJMSRecipient()JMSToJXTAMsgConverter 類
JMSToJXTAMsgConverter 是消息格式轉換器類,它采用了一個 JMS 消息,并生成一個 JXTA 消息。JMSToJXTAMsgConverter 類包含下面介紹的這些方法。
JMSToJXTAMsgConverter 構造函數
現在看看清單 9,它展示了 JMSToJXTAMsgConverter 構造函數。
清單 9. JMSToJXTAMsgConverter 構造函數 public JMSToJXTAMsgConverter ( String sender, TextMessage jmsMsg ) throws JMSException { StringMessageElement smeSender = null; StringMessageElement smeMessage = null; jxtaMessage = new net.jxta.endpoint.Message (); smeSender = new StringMessageElement ("JMSSender", sender, null); smeMessage = new StringMessageElement ("Message", jmsMsg.getText(), null); jxtaMessage.addMessageElement (smeSender); jxtaMessage.addMessageElement (smeMessage); }//JMSToJXTAMsgConverter constructorJMSToJXTAMsgConverter 構造函數幾乎執行這個類所要求的所有處理。它有兩個參數,第一個參數是發送 JMS 消息的 JMS 客戶機的名稱。在構造函數JMSToJXTAMsgConverter 中生成的 JXTA 消息里加入發送者的名字。這使 J2ME 接收者知道是哪一個 JMS 客戶機發送的消息。第二個參數是以TextMessage 對象形式出現的 JMS 消息。
JXTA 消息是根據收到的 JMS 消息生成的。JXTA 消息由幾個消息元素組成。因此,我首先分析 JXTA 消息中每個元素的內部細節。
JXTA 消息元素的結構類似于(第一篇文章的“中繼器和 JXME 客戶機”一節中介紹的)JXME 消息元素的結構。
JXTA 消息元素包含以下 4 個字段:
消息元素的名字。 在這里,在一個名為 “Message” 的元素中包裝 JMS 消息。與此類似,將 JMS 發送者的名字包裝到一個名為“JMSSender” 的元素中。注意,元素的名稱是可選的,因此可以忽略它。不過,為每個元素命名會使處理變得直觀而且更容易實現。可選的消息元素 MIME 類型。 如果沒有指定這個字段,那它的值就假定為 “Application/Octet-Stream”。在這里,所生成的消息的 MIME 類型是“text/plain”。要發送的數據。 消息元素,如包含 JMS 消息內容的消息元素,包含 JMS 發送者名字的 sender 元素等。可選的簽名。 這個字段包含消息元素的加密簽名。目前,JXME 還不支持在 J2ME 一端添加簽名。因此,在生成 JXTA 消息時沒有必要添加簽名。您已經見過了 JXTA 消息元素的結構。JXTA 實現有一個名為 MessageElement 的類,它處理所有類型的 JXTA 消息元素。此外,還有一些從MessageElement 類派生的子類。每一個子類處理一種特定類型的消息元素。例如,StringMessageElement 類處理攜帶文本字符串的消息元素(這種消息的 MIME 類型是“text/plain”)。
用 StringMessageElement 類生成 JXTA 消息的每個消息元素。將 JMS 消息發送者的名字包裝到名為 smeSender 的 StringMessageElement 對象中。與此類似,將 JMS 消息內容包裝到名為 smeMessage 的另一個 StringMessageElement 對象中。
要用 StringMessageElement 類生成一個消息,必須先調用 StringMessageElement 類的構造函數實例化這條消息。這個構造函數有三個參數。第一個參數指定消息元素的名稱,第二個參數指定元素數據的內容,第三個參數指定加密簽名。我們沒有使用簽名,因此傳遞 null 作為第三個參數的值。
分析一下清單 9 中的JMSToJXTAMsgConverter 構造函數。先實例化名為smeSender 和smeMessage 的兩個StringMessageElement 對象。在創建smeMessage 對象時,需要使用傳入的 JMS 消息的內容。可以用TextMessage 對象的getText() 方法提取 JMS 消息內容,然后將內容傳遞給 StringMessageConstructor,以便生成smeMessage 對象。
現在,需要將這兩個 StringMessageElement 對象包裝到一個 JXTA 消息中。為此,必須先實例化一個名為 jxtaMessage 的 net.jxta.endpoint.Message 對象,該對象表示了一個完整的 JXTA 消息。然后,需要兩次調用它的addMessageElement() 方法,對每個StringMessageElement 對象調用一次。
getMessage() 方法
用清單 10 所示的 getMessage() 方法提取 JXTA 消息。這個方法只返回在構造函數中準備的 jxtaMessage 對象。
清單 10. getMessage() 方法 public Message getMessage () { return jxtaMessage; }//getMessage()JMSSearch 類
JMSSearch 類在 JMS 網絡中進行搜索,就像 JXTASearch 在 JXTA 網絡中搜索一樣。不過,在 JMS 網絡中搜索是相當簡單的,因為可以使用 Java Naming and Directory Interface(JNDI)搜索不同的 JMS 資源。您可以用 JNDI 搜索像消息對象(隊列和連接工廠)、數據庫和 mail 會話這樣的資源。
這里用 JNDI 搜索隊列。看一下清單 11 中的 JMSSearch 類。
清單 11. JMSSearch 類public class JMSSearch{ public JMSSearch (){ } public QueueSender getQueueSender ( String queueName, QueueSession queueSession ) { InitialContext jndiContxt = null; Queue outgoingQueue= null; QueueSender qSender = null; try { jndiContxt = new InitialContext (); outgoingQueue= (Queue) jndiContxt.lookup ( queueName ); qSender = queueSession.createSender (outgoingQueue); }//try catch ( Exception e ) { e.printStackTrace (); }//catch return qSender; }//getQueueSender()}//JMSSearchJMSSearch 類只包含一個方法 getQueueSender()。這個方法有兩個參數:要搜索的隊列名和一個QueueSession 對象。這與前面對JXTAToJMSMsgConverter 的getQueueSession() 方法進行的討論中介紹的QueueSession 對象是一樣的。我會解釋將QueueSession 對象作為參數傳遞的目的。
getQueueSender() 方法為這個特定的隊列名返回 QueueSender 對象。
正如在清單 11 中看到的,getQueueSender() 方法的實現很簡單。首先,它實例化一個 InitialContext 對象。這是 JNDI 的要求。InitialContext 對象包含進行搜索操作的方法。
然后,只需調用 InitialContext 類的 lookup() 方法即可。在 JNDI 術語中,搜索被稱為查找(lookup)。lookup() 方法以隊列名作為參數,返回一個表示所搜索隊列的Queue 對象。Queue 對象被命名為outgoingQueue,因為這個Queue 對象表示消息接收者的 JMS 隊列。在后面要用這個隊列發送輸出消息。例如,假設 Alice 正在路上,帶著她的移動端 JXTA4JMS,并向 Bob 發送了一條消息。她的桌面端 JXTA4JMS 收到通過 JXTA 網絡傳來的這條消息,然后在 JMS 網絡中搜索 Bob 的隊列,將 Bob 的隊列作為outgoingQueue 對象處理,同時將 Alice 的消息發送給(Bob 的)outgoing 隊列。
現在您認識到了在隊列名前面加上前綴 “jms/” 的重要性了吧(回想一下在 JXTAToJMSMsgConverter 類的getJMSRecepient() 方法前添加的“jms/” 前綴)。JNDI 查詢不僅搜索 JMS 資源(其他服務端模塊用它搜索非 JMS 資源,如數據庫),所以用某種標識符標識出屬于特定組的資源(例如,所有屬于 JMS 網絡的資源)很重要。如果不在隊列名前加上字符串前綴,那么可能會將隊列名與一些其他非 JMS 網絡資源相混淆(如數據庫表)。
盡管搜索了所需要的 Queue 對象,但是這里還有一步操作要做。要用搜索到的 Queue 對象創建一個 QueueSender 對象。然后,Router 類就用這個 QueueSender 對象發送 Queue 中的消息。
要創建 QueueSender 對象,還需要一個 QueueSession 對象。這個 QueueSession 對象應當與在JXTAToJMSMsgConverter 類中生成 JMS 消息時使用的那個對象相同。Router 類同時使用JXTAToJMSMsgConverter 和JMSSearch 類,所以在下面討論Router 時,我將說明它如何確保將正確的QueueSession 對象傳遞給getQueueSender() 方法。
在 getQueueSender() 方法中做的最后一件事是調用 QueueSession 對象的 createSender() 方法。這個方法用我們剛剛創建的 Queue 對象作為參數,并返回一個 QueueSender 對象。QueueSender 現在就完成了它該做的操作。如果通過這個 sender 對象發送 JMS 消息,那么消息會自動達到正確的接收者。現在,看一下Router 類如何使用剛才創建的所有底層支持來完成路由。
Router 類
Router 類是 JXTA4JMS 通信的通信樞紐。Router 類使用一些低級的層(包括格式轉換和網絡),并在 JMS 網絡與 JXTA 網絡之間來回發送消息。Router 類的作用是雙重的:
它將從 JMS 網絡收到的消息發送給移動客戶機。 同時將從 JXTA 網絡接收到的消息發送給 JMS 客戶機。因此,它包含兩個方法:sendMessageToMobile() 和 sendMessageToJMS()。Listener 類在收到來自 JMS 客戶機的消息時調用sendMessageToMobile() 方法。sendMessageToMobile() 方法通過 JXTA 網絡向 J2ME 客戶機發送消息。與此類似,Listener 類在收到來自移動客戶機的消息時使用sendMessageToJMS() 方法。
清單 12 顯示了 Router 構造函數的實現。
清單 12. Router 構造函數 public Router ( String peerName, PeerGroup peerGroup ) { this.peerName = peerName; this.peerGroup = peerGroup; }//RouterRouter 構造函數的實現很簡單。它只采用了兩個參數(peer 的名字和它的 peer 組),并存儲這些參數,以便 Router 類的兩個方法使用它們。
sendMessageToMobile() 方法
清單 13 展示了 sendMessageToMobile() 方法的實現。
清單 13. sendMessageToMobile() 方法 public void sendMessageToMobile( String sender, OutputPipe outputPipe, TextMessage jmsMessage ) { try { JMSToJXTAMsgConverter jxtaConverter = new JMSToJXTAMsgConverter ( sender, jmsMessage ); net.jxta.endpoint.Message jxtaMessage = jxtaConverter.getMessage (); outputPipe.send ( jxtaMessage ); }//try catch ( Exception e ) { e.printStackTrace (); }//catch }//sendMessageToMobile()sendMessageToMobile() 方法有三個參數:
JMS 發送者的名字,我們將發送者的 JMS 消息發送給移動客戶機。一個表示 JXTA 管道的 OutputPipe 對象。(移動客戶機監聽這個管道的另一端,因此通過這個管道發送消息。)在后面討論Listener 類時,我將說明創建這個OutputPipe 對象的機制。要發送給移動客戶機的 JMS 消息。很容易猜到,您必須做的就是將 JMS 消息從 JMS 格式轉換為 JXTA 格式,然后通過輸出管道發送這個消息。
因此,正如在清單 13 的sendMessageToMobile() 方法中看到的,首先實例化一個JMSToJXTAMsgConverter 對象,并用它獲得傳入的 JMS 消息的 JXTA 表達。然后,只需調用OutputPipe 對象的send() 方法,同時傳遞 JMS 消息即可。OutputPipe 對象處理通過 JXTA 網絡發送消息的低級過程。
sendMessageToJMS() 方法
現在看一下清單 14,它展示了 sendMessageToJMS() 方法的實現。
清單 14. sendMessageToJMS() 方法 public void sendMessageToJMS( QueueConnectionFactory qConnFactory, net.jxta.endpoint.Message jxtaMessage ) { try { JXTAToJMSMsgConverter jmsConverter = new JXTAToJMSMsgConverter ( qConnFactory, jxtaMessage ); String jmsRecipient = jmsConverter.getJMSRecipient(); TextMessage jmsMessage = jmsConverter.getMessage(); QueueSession queueSession = jmsConverter.getQueueSession(); JMSSearch jmsSearch = new JMSSearch (); QueueSender qSender = jmsSearch.getQueueSender ( jmsRecipient, queueSession ); qSender.send ( jmsMessage); }//try catch ( Exception e ){ e.printStackTrace (); }//catch }//sendMessageToJMSsendMessageToJMS() 方法采用了兩個參數:QueueConnectionFactory 對象和將發送給 JMS 接收者的 JXTA 消息。回想一下,JXTAToJMSMessageConverter 構造函數也要求使用同樣的兩個對象。因此,sendMessageToJMS() 方法要實例化一個JXTAToJMSMsgConverter 對象,然后調用JXTAToJMSMsgConverter 類的 getJMSRecepient() 和 getMessage() 方法,以便分別提取 JMS 消息的發送者的名字,以及所收到的 JXTA 消息的 JMS 格式。
sendMessageToJMS() 方法還調用了 JXTAToJMSMsgConverter 類的 getQueueSession() 方法。如前所述,這個方法將返回 QueueSession 對象。
然后 sendMessageToJMS() 方法實例化了一個 JMSSearch 對象,并調用該對象的 getQueueSender() 方法。getQueueSender() 方法用 JMS 消息接收者的名字(一個隊列名)和QueueSession 對象作為參數。它返回一個QueueSender 對象(已在討論JMSSearch 類時介紹)。
最后,調用 QueueSender 對象的 send() 方法。send() 方法采用轉換后的 JMS 消息,并將這條消息發送給 JMS 接收者。
Listener 類
Listener 類被設計成一個連續監聽模塊。需要將這個類設計為在啟動后同時監聽 JMS 和 JXTA 網絡。當它收到 JMS 消息后,立即將這個消息通過 JXTA 網絡轉發給 J2ME 設備。而當它從 JXTA 網絡收到 J2ME 設備發來的消息后,它會立即將這個消息轉發給相關的 JMS 客戶機。
Listener 類實現了一個 run() 方法,該方法在單獨的線程中執行。這個線程不間斷地監聽來自 JMS 和 JXTA 網絡的消息。我將展示如何在run() 方法中實現 JMS 和 JXTA 消息的處理邏輯。Listener 類包含一些專用輔助方法。這些方法有助于創建輸入和輸出 JXTA 管道。
createPipeAdvertisement() 方法
清單 15 中展示的 createPipeAdvertisement() 方法是一個專用輔助方法,它將創建一個管道廣告,并以 PipeAdvertisement 對象的形式返回這個廣告。
清單 15. createPipeAdvertisement() 方法 private PipeAdvertisement createPipeAdvertisement (String peerName) { PipeAdvertisement pipeAd = null; try { String fileName = peerName+".xml"; File file = new File ( fileName ); if ( file.exists() ) { FileInputStream is = new FileInputStream (file); if ( is.available() > 0 ) { pipeAd = (PipeAdvertisement) AdvertisementFactory.newAdvertisement( new MimeMediaType("text/xml"), is ); }//if ( is.available() > 0) } else { pipeAd = (PipeAdvertisement) AdvertisementFactory.newAdvertisement ( pipeAd.getAdvertisementType () ); pipeAd.setName (peerName); pipeAd.setType ("JxtaUnicast"); pipeAd.setPipeID( (ID)net.jxta.id.IDFactory.newPipeID( netPeerGroup.getPeerGroupID() ) ); FileOutputStream os = new FileOutputStream ( fileName ); os.write ( pipeAd.toString().getBytes() ); os.close(); }//end of else return pipeAd; }//try catch (Exception ex) { ex.printStackTrace (); }//catch return null; }//createPipeAdvertisement()創建管道廣告就是一個生成 XML 的任務。需要根據由 JXTA 協議定義的廣告格式生成正確的 XML 代碼。觀察清單 16,它展示了管道廣告的 XML 結構。
清單 16. 一個 XML 格式的管道廣告示例 urn:jxta:uuid-59616261646162614E50.....91E04 JxtaUnicast AliceJMSJXTA 提供了方便您生成 JXTA 廣告的類。createPipeAdvertisement() 方法首先檢查管道廣告是否(由于以前的管道創建操作)已經存在于一個文件中。如果存在這樣的文件,那么只需打開該文件并在文件輸入流中讀取其內容即可。然后,用一個名為AdvertisementFactory 的 JXTA 類從輸入流創建一個廣告。
AdvertisementFactory 類有一個名為 newAdvertisement() 的靜態方法,它有兩個參數。第一個參數指定所創建廣告的 MIME 類型。在這里,要創建一個 XML 廣告,所以 MIME 類型應當是“text/xml”。第二個參數指定從文件中創建的輸入流。
newAdvertisement() 方法返回一個 Advertisement 對象,在將它返回給調用應用程序之前,可以將它強制轉換為PipeAdvertisement。
現在,看一看沒有管道廣告時如何做。當然,必須從頭開始生成管道廣告。為此,要重載 AdvertisementFactory 類的 newAdvertisement() 方法。這個方法只采用了一個參數,它指定要創建的廣告的類型。newAdvertisement() 方法返回Advertisement 形式的管道廣告。將Advertisement 對象強制轉換為PipeAdvertisement 對象。
現在設置剛創建的新管道廣告的三個參數。這三個參數是要宣傳的管道的名字、管道的類型(在這里是 JXTAUnicast)和管道標識符。我們已經知道前兩個參數,但是需要一個名為IDFactory 的標識符工廠來創建新的標識符。這個工廠幫助創建惟一的標識符。
完成了新的廣告后,將它保存到磁盤文件中并返回 PipeAdvertisement 對象。
publishPipeAdvertisement() 方法
清單 17 所示的 publishPipeAdvertisement() 方法是一個專用輔助方法,它采用了一個 PipeAdvertisement 對象,并通過 JXTA 網絡發布它。
清單 17. publishPipeAdvertisement() 方法 private boolean publishPipeAdvertisement (PipeAdvertisement pipeAd) { DiscoveryService discSvc = netPeerGroup.getDiscoveryService (); discSvc.remotePublish ( pipeAd, DiscoveryService.ADV, DiscoveryService.NO_EXPIRATION ); return true; }//publishPipeAdvertisement()已經在一個 peer 組發布了管道廣告。而且,所有廣告都有一個失效時間。因此,這個廣告會在特定的時間段內出現在特定的 peer 組中。
JXTA 提供了一種稱為管道服務的服務,可以用它來發布管道廣告。JXTA 實現在一個名為 PipeService 的類中包裝了管道服務的功能。可以通過調用PeerGroup 對象的getPipeService() 方法實例化PipeService 類。
只需調用 PipeService 類的 remotePublish() 方法就行了。remotePublish() 方法在 JXTA 網絡上發布廣告。它有三個參數(要發布的PipeAdvertisement 對象、廣告的類型和廣告的失效時間)。
createInputPipe() 方法
清單 18 所展示的 createInputPipe() 方法采用了一個 peer 名,并創建一個輸入管道來監聽收到的消息。
清單 18. createInputPipe() 方法 private void createInputPipe (String peerName) { PipeAdvertisement pipeAd = createPipeAdvertisement (peerName); if (pipeAd!= null) { boolean published = publishPipeAdvertisement ( pipeAd ); if (published) { PipeService pipeSvc = netPeerGroup.getPipeService (); try { inputPipe = pipeSvc.createInputPipe ( pipeAd ); }//try catch (IOException io) { io.printStackTrace (); }//catch }//if(published) }//if (pipeAd!= null) }//createInputPipe()createInputPipe() 方法分三步創建一個輸入管道:
它調用 createPipeAdvertisement() 方法,同時傳遞 peer 標識符。createPipeAdvertisement() 方法返回一個已經討論過的PipeAdvertisement 對象。然后它調用 publishPipeAdvertisement() 方法,同時傳遞 PipeAdvertisement 對象。publishPipeAdvertisement() 方法在 JXTA 網絡上發布廣告。最后,它在發布的 PipeAdvertisement 對象上創建一個 InputPipe 對象。為此,只要調用PipeService 類的createInputPipe() 方法,就會返回一個PipeService 對象。createInputPipe() 設置新創建的 InputPipe 對象為一個類級的對象。在后面用這個 JXTAInputPipe 對象監聽收到的 JXTA 消息。
searchAndCreateOutputPipe() 方法
已經介紹了如何創建一個輸入 JXTA 管道。現在,看一看如何創建一個輸出 JXTA 管道。
清單 19 中所見的 searchAndCreateOutputPipe() 方法用一個 peer 名作為參數,并為這個 peer 創建一個輸出管道。
清單 19. searchAndCreateOutputPipe() 方法 private void searchAndCreateOutputPipe(String peerName) { try { JXTASearch jxtaSearch = new JXTASearch (netPeerGroup); PipeAdvertisement pipeAdvert = jxtaSearch.getPipeAdvertisement (peerName); if (pipeAdvert != null) outputPipe = jxtaSearch.getOutputPipe(pipeAdvert); }//try catch ( Exception ex ) { ex.printStackTrace (); }//catch }//searchAndCreateOutputPipe()創建輸出管道與創建輸入管道的過程類似。創建輸入管道時,首先創建一個 PipeAdvertisement 對象,然后發布它。但是在創建輸出管道對象時,首先搜索已經發布的PipeAdvertisement 對象。得到PipeAdvertisement 對象后,可以用與創建輸出管道類似的過程創建輸入管道。
因此,創建輸出管道包括兩步:搜索已發布的管道廣告,然后用管道廣告創建輸出管道。在 JXTASearch 類中已經有了完成這兩項任務的函數。
調用 JXTASearch 類的 getPipeAdvertisement() 方法,同時傳遞 peer 的名字。getPipeAdvertisement() 方法返回PipeAdvertisement 對象的名字。
在第二步中,調用 JXTASearch 類的 getOutputPipe() 方法。getOutputPipe() 方法采用了PipeAdvertisement,并
來源:php中文網