廣告處理
在我們建立了發現監聽器之後, 它將不停的加入一些新發現的module說明通告到我們的本地緩衝中。 每次processPrimes()方法被調用的時候, 客戶peer將嘗試連接module說明通告代表的peer, 接入到他們的輸入通道中,傳遞一個消息去初始化這個peer的質數發現服務。) 這個方法的第一個元素就是決定我們能夠委託以工作的peer集合, 應該記得一個通告有一個期限限制, 因此我們要消除那些不在有效的通告。 Public int[] processPrimes(int low, int high) { Set setCopy = null; synchronized(adverts) { Set setCopy = (Set) adverts.clone(); } ArrayList workingList = new ArrayList(); ArrayList expired = new ArrayList(); long currentTime = System.getCurrentTimeMillis(); Iterator it = workingSet.iterator(); while (it.hasNext()) { ModuleSpecAdvertisement ad = (ModuleSpecAdvertisement)it.next(); if (ad.getLocalExpirationTime() > currentTime + (2 * 60 *1000)) { workingList.addElement(ad); } else { expired.addElement(ad); } } removeExpired(expired); 前述的程序段執行了對一個簡單緩衝中被發現的服務的管理, 讓removeExpired()方法去刪除那些過期和即將過期的通告(這裏沒有詳細說明removeExpired())。 當我們有了一個有效通告的集合之後,我們能夠開始對它們進行操作以獲得我們需要用來發送消息的管道通告。 . 注意這個工作的分發是人爲的: 一些peer可能比其它peer更有能力一些, 一些又可能 網絡連接方面更好些。 這些不同應該在分配工作的時候都被考慮到, 事實上, 也不會故意把工作分配爲過多的片段, 因爲網絡通信時間可能比花在實際計算質數的時間更多。 (這個例子的目的是說明怎麼從一個ModuleSpecAdvertisement得到一個管道通告, 這樣創建一個新的消息, 然後怎樣通過管道發送一個消息。) Listing 16.14展示了這些自然數列是如何被分爲一個一個的子列的,每個子列又能和一個將送往別的peer的消息相對應。 消息被插入到一個hash表映射中, 映射的鍵值顯示了消息的狀態: 是否已經發送了? 我們是否收到了它的結果? Listing 16.14 Creating New Messages Map messageMap = new HashMap(); int size = workingList.size() int mod = high % size; high -= mod; int perPiece = high / size; for (int i=0; i < size; i++) { //create a new message Message msg = pipeSvc.createMessage(); msg.setString(ServiceConstants.LOW_INT, low); //last message will get to compute a bit more if (i == size-1) { high = low + perPiece ?1 + mod; } else { high = low + perPiece -1; } msg.setString(ServiceConstants.HIGH_INT, high); low += perPiece;
//we neither sent the message, nor did we get a response StatusMap statusMap = new StatusMap(false, false); StatusMap statusMap = new StatusMap(false, false); messageMap.put(statusMap, msg); } StatusMap 就是一對布爾值,這裏並沒有列出 我們的最後一步就是從每個ModuleSpecAdvertisement中提取管道通告, 打開每個管道, 發送一個消息到那管道, 然後將這個消息標記爲“已經發送”。 應記得一個通告就是一個結構化的文檔, 類似於XML文檔, 它能輕易的被轉換爲一個文本文檔然後打印出來, 在開發和測試的時候查閱通告的內容是非常有好處的。 Listing 16.15 Printing an Advertisement Collection ads = messageMap.values(); Iterator it = ads.iterator(); while (it.hasNext()) { ModuleSpecAdvertisement ad = (ModuleSpecAdvertisement)it.next(); //First, print out ModuleSpec advertisement on standard output StructuredTextDocument doc = (StructuredTextDocument)ad.getDocument(new MimeMediaType ("text/plain")); try { StringWriter out = new StringWriter(); doc.sendToWriter(out); System.out.println(out); out.close(); } catch (IOException e) { } ... 正如我們先前討論的, 一個StructuredTextDocument類包括了很多元素,其中一個是一個參數。 當我們爲我們的服務構造了ModuleSpecAdvertisement的時候,我們將它作爲參數進入這個服務的管道通告。這個參數正好是另外一個StructuredDocument元素, 我們可以用操作XML文檔一樣的方法 操縱它。 在解析通告的參數元素的過程中,我們首先獲得這個通告的ID 和類型, 通道的ID與URN說明相統一, 在JXTA 說明中有概述, JXTA說明將管道的特殊鑑定器用128-bit編碼, 下面是這個URN的一個例子。 urn:jxta:uuid-59616261646162614E5047205032503382CCB236202640F5A242ACE15A8F9D7C04 IDFactory類有能力根據每個URN建造一個PipeID對象。 這就是我們用pipe ID去關聯一個pipe通告的機制。 Listing 16.16 Working with Advertisement Parameters StructuredTextDocument param = (StructuredTextDocument)ad.getParam(); String pipeID = null; String pipeType = null; Enumeration en = null; if (param != null) { en = param.getChildren("jxta:PipeAdvertisement"); } Enumeration child = null; if (en != null) { child = ((TextElement)en.nextElement()).getChildren(); } if (child != null) { while (child.hasMoreElements()) { TextElement el = (TextElement)child.nextElement(); String elementName = el.getName(); if (elementName.equals("Id")) { pipeID = el.getTextValue(); } if (elementName.equals("Type")) { pipeType = el.getTextValue(); } } } if (pipeID != null || pipeType != null) { PipeAdvertisement pipeAdvert = (PipeAdvertisement) AdvertisementFactory.newAdvertisement( PipeAdvertisement.getAdvertisementType()); try { URL pidURL = new URL(pipeID); PipeID pid = (PipeID)IDFactory.fromURL(pidURL); pipeAdvert.setPipeID(pid); } catch (MalformedURLException e) { System.out.println("Wrong URL: " + e.getMessage()); return; } catch (UnknownServiceException e) { System.out.println("Unknown Service: " + e.getMessage()); return; } } 基於這個PipeAdvertisement, 我們現在有能力去構造一個輸出管道去連接遠程peer的輸入管道了。 如Listing 16.17, 應該記得一個管道是一個單向的溝通渠道,因此我們沒有期望從這個管道上獲得遠程peer的回執, 遠程peer進行一個必要的類似的工作, 打開一個管道連接回客戶端, 然後連同結果一起發回消息。 Listing 16.17 Creating an Output Pipe try { outputPipe = pipeSvc.createOutputPipe(pipeAdvert, 30000); outputPipe.send(msg); System.out.println("Sent message on output pipe"); } catch (IOException e) { System.out.println("Can't send message through pipe: " + e.getMessage()); } } 有趣的是,這個管道創建機制是:一個peer可能在發送ModuleSpecAdvertisement和一個客戶連接到它之間改變網絡身份, 雖然如此,這個peer的虛擬身份在JXTA網絡上將不會改變, 運行時的服務保證被ModuleSpecAdvertisement所通告的管道一直處於連接狀態。 爲了開啓應用服務, 確定所有的JXTA類都在classpath裏面, 然後輸入下面這個命令。 java primecruncher.PrimePeer 你也可以運行下面的參數, 這些參數允許你饒過JXTA的登入界面。 java –D net.jxta.tls.principal=USERNAME -Dnet.jxta.tls.password=PASSWORD primecruncher.PrimePeer By substituting your JXTA username and password you can run the client similarly:(取代你的JXTA用戶名和密碼,類似的你可以運行客戶端。) java -Dnet.jxta.tls.princincipal=USERNAME -Dnet.jxta.tls.password=PASSWORD primecruncher.PrimeClient
這是介紹P2P的簡單開發的文檔, 包括了一些大概意思,希望對大家有一點點幫助,我的翻譯水平也有限,希望得到諒解。
|