使用 Rational Team Concert Item Connector 同步資料儲存庫

myattitude發表於2010-08-03
Ken Kumagai, 軟體工程師, IBM
Yoshio Horiuchi, 軟體工程師, IBM

簡介: 不同的部門系統之間的資料同步化是非常重要的。例如,客戶支援與開發部門通常擁有他們自己的問題追蹤系統,這樣資料同步化就能使不同部門之間的員工可以共享資訊並且同時處理相同的問題。但是,這實現起來是十分困難的,因為它需要手動的操作。不同部門之間的員工協作性地處理相同問題的一種方式是,聯絡兩個團隊之間的儲存庫,這樣人們相互之間可以共享並追蹤儲存庫中的物件。您可以使用專案聯結器(Item Connector)來完成該項操作,其中的專案聯結器是 IBM® Rational Team Concert™ 中一個可以擴充套件的框架,它將外部儲存庫中的物件與基於 IBM® Jazz™ 技術儲存庫中的永久性物件聯絡了起來。本文向您解釋了怎樣設計並建立新的聯結器,而且筆者提供了一些範例以便學習。

專案聯結器工作的方式

當您得到來自使用者的訪問時,您該怎樣回應呢?這完全取決於使用者。如果它是一個外部性的使用者,那麼支援人們接到一個訪問然後建立一個憑證以記錄這次訪問。這叫做一個 訪問追蹤系統 。使用者可能是一個內部性的使用者,它沒有接受到來自執行系統的警告。在這種情況下,內部支援員工會得到訪問,而問題會得到 問題追蹤系統 的追蹤。這些都是 追蹤系統 的例項。

最近的追蹤系統很依賴於軟體,這樣報告的問題會最終帶到軟體開發專案中。但是,怎樣將這些問題與開發活動聯絡起來呢?這是一個主要的陷阱。在很多情況下,這些問題可以得到手動的解決。例如,資訊可以通過電子郵件傳送到軟體產品中,或者通過電話來進行討論。重要的資訊很容易丟失。因為在開發和前端問題追蹤系統之間沒有聯絡,時間會浪費在處理錯誤上(圖 1)。


圖 1. 資訊通常是無法追蹤的
沒有專案聯結器的工作流程圖

IBM® Rational Team Concert™ 提供了一種協作性的開發環境以處理這些情況。它是一個開放性的平臺,可擴充套件的設計。在 Jazz.net 上已經可以得到各種不同的聯結器。但是,您的公司可能會使用一個自己的系統,該系統沒有與任何軟體開發系統相聯絡。使用 Rational Team Concert 軟體處理儲存庫沒有障礙。Item Connector 是處理這樣程式碼最佳的方法。(檢視 參考資料 中對“建立一個新的 Item Connector”的連結)

Item Connector 是 Rational Team Concert 內一種可擴充套件的框架,它將外部性的儲存庫與 Jazz 儲存庫中的 Jazz 專案(“專案”是 Item Connector 中的一個術語用於代表基於 Jazz 儲存庫的永久性物件)聯絡了起來。連線是通過首先將外部性物件複製到 Jazz 儲存庫中,然後將外部性物件與 Jazz 專案的狀態進行同步化來實現的。這就是所謂的同步化過程。一個公司可以使用它來自動將系統中的資訊,例如問題追蹤系統,與軟體開發活動聯絡起來。系統相互之間聯絡起來並提供了追蹤性(圖 2)。


圖 2. 使用 Item Connector 來追蹤資訊
使用 Item Connector 的流程圖

本文向您解釋了怎樣開發新的聯結器。它涉及到了四個步驟:

  1. 設計總體的結構。
  2. 準備環境以及範例原始碼。
  3. 設計 Jazz 儲存庫與服務。
  4. 擴充套件 Item Connector 的構件。

Jazz 團隊伺服器

Jazz™ 團隊伺服器基於 Eclipse Equinox 框架,它是 OSGi 的實施。通過載入外掛可以輕鬆新增一個新的服務。Jazz 團隊伺服器提供了擴充套件點,這樣開發員就可以建立並註冊他們自己的外掛了。

專案聯結器構件

重點提示

本文基於 Rational Team Concert 1.0.1 版本。關於 2.0 版本或者後續版本,請參考 Resources 部分中的每一個構件頁面。

專案聯結器是 Rational Team Concert 的框架。它提供了擴充套件點以將外部性的儲存庫與 Jazz 儲存庫中的 Jazz 專案聯絡起來(提示:專案 意思是 Jazz 儲存庫中的一個永久性物件)。如果您想要得到更多資訊,那麼您可以檢視 參考資料 中對“建立一個新專案聯結器”的連結。專案聯結器一個最顯著的特性是代理機制。外部性的物件會首先複製到代理物件中,然後代理物件才會複製到背景中的 Jazz 專案。為了實施這種機制,專案聯結器提供了三種特定的 Jazz 專案:ExternalProxy、ExternalState 與 SyncRule。

  • ExternalProxy 含有對外部性物件與 Jazz 物件的連線。
  • ExternalState 是一個外部性物件屬性的容器。
  • SyncRule 含有同步性規則,它將外部性屬性名對映到 Jazz 屬性名。

Item Connector 由這些構件組成,在接下來的章節中將分別進行介紹:

  • 專案聯結器客戶端
  • 專案管理器
  • 外部儲存庫管理器
  • 值轉換器

Item Connector 客戶端

Item Connector 客戶端建立了一個外部性物件與一個 ExternalProxy 專案之間的聯絡,並使用以下七個步驟來向 Jazz 儲存庫傳送物件的資料(檢視 參考資料 中“建立一個新的專案聯結器”的連結):

  1. 獲取或者建立物件的 ExternalProxy 專案。
  2. 獲取或者建立物件的 ExternalState 專案。
  3. 獲取 SyncRule 專案。
  4. 使用 SyncRule 來決定應該向 Jazz 儲存庫傳送什麼屬性。
  5. 將屬性複製到 ExternalState 專案中。
  6. 將 SyncRule 專案附屬到 ExternalProxy 專案中。
  7. 儲存 ExternalState 專案與 ExternalProxy 專案。

專案管理器

專案管理器是一種伺服器端的外掛,可以通過 com.ibm.team.interop.service.itemManager 擴充套件點獲得(圖 3)。專案管理器會建立 Jazz 專案或者更新已經被建立的 Jazz 專案的屬性。


圖 3. 專案管理器的擴充套件點
 New Extension 介面

外部儲存庫管理器

外部儲存庫管理器也是一種伺服器端的外掛,可以通過 com.ibm.team.interop.service.externalManager 擴充套件點獲得(圖 3)。外部儲存庫管理器可以建立、獲取和更新外部性物件。

值轉換器

您可以從 com.ibm.team.interop.service.valueTransformer 擴充套件點處獲得值轉換器。該構件的具體內容超出了本文的討論範圍。如果您想要得到更多資訊,那麼您可以檢視文獻中的“值轉換器實施(檢視 參考資料 中所列出“建立一個新的專案聯結器”的連結)。

設計總體架構

在您可以設計專案與包結構之前,您需要選擇一個開發模式。

開發模式

該程式同樣在“建立一個新的專案聯結器”章節中有所描述(您可以檢視 參考資料)。其中提供了四個場景,及其在外部性伺服器中的前提條件。但是,決定場景對於新專案直接可用是很困難的,因為有四個描述去決定總體的結構。這裡,我們只關注專案聯結器客戶端的開發。對於開發,您必須決定目標 Java™ Virtual Machine (VN),專案聯結器就是在它上面執行的。可能有三種 Jazz VNs:一個外部性的伺服器,一個 Jazz 伺服器,或者一個獨立的伺服器。這就是所謂的“開發模式”。

  • 整合到一個外部性的伺服器中
    • 與“儲存”操作一起執行的端效果
  • 位於一個 Jazz 伺服器中
    • 等待並響應來自外部性伺服器的更新通知
    • 階段性地查詢更新
  • 獨立(與外部性 Jazz 伺服器或者 Jazz 伺服器相隔離)
    • 在請求時啟用過程(客戶端)
    • 階段性地查詢更新(伺服器)

模式 1.1、2.1、2.2 與 3.1 分別對應於四種場景,但是對於 3.2 沒有場景。重要的因素是選擇一個開發模式來決定總體的結構。表 1 總結了專案聯結器客戶端的開發,以及開發模式中外部性伺服器的前提條件。


表 1. 開發模式中的部署與前提條件

開發模式 Java VM 的開發 外部性伺服器的前提條件
1.1 使用外部儲存庫 可定製的儲存操作
2.1 使用 Jazz 儲存庫 事件通知機理
2.2 使用 Jazz 儲存庫 得到及設定資料的服務
3.1 獨立 得到並設定資料的服務
3.2 獨立 得到並設定資料的服務

圖 4 是一個開發模式 3.1 的結構性概述。對於這種開發,專案聯結器客戶端會隔離於 Jazz 或者外部性伺服器而執行。在這種結構中,我們假設外部性的伺服器提供了控制外部儲存庫的服務。為了提供服務,專案聯結器客戶端會訪問外部性伺服器中的 getAllObjects 服務,因為該構件獲得了所有的外部性物件,然後專案聯結器就可以執行操作以建立或者更新相應的 Jazz 專案了。反過來,外部儲存庫管理器會一個接一個地選擇 Jazz 專案,並執行操作以一個接一個地建立或者更新相應的外部性物件,所以該評論使用外部性伺服器中的 getObjectpostObject,以及putObject 服務。


圖 4. 模式 3.1 的結構性概述
流程圖

在本文的稍後部分中,我們將會向您解釋模式 3.1 的執行過程。

包設計

在選擇一個開發模式之後,您就可以為將會實現模式的所有內容而設計專案以及包結構了。表 2 顯示了對於執行模式 3.1 或者 3.2 來說典型的一些結構。有一個 Java 專案以及四個外掛專案。這些專案起的名字用以匹配相應的包名。我們使用“類棧”作為使用專案和包所建立的範例的名字。


表 2. 新聯結器的專案名與內容

包(Java project) 內容
ClassicTalkConnectorClient 專案聯結器客戶端
包 (外掛專案) 內容
com.example.classictalk.common Jazz 儲存庫與服務(介面)
com.example.classictalk.interop.managers 專案管理器,外部儲存庫管理器
com.example.classictalk.launches 啟動檔案
com.example.classictalk.service 服務(執行)

準備環境以及範例原始碼

注意:
在開始學習下一個章節之前,我們會參考文獻中的相應的章節(參考資料),來為開發一個 Jazz 儲存庫與服務建立一個環境。您所需要的所有程式碼都可以從這裡的下載資源( 下載)中獲得。您可以使用本文來學習程式碼,或者只是從下載資源中所包含的範例處開始。

建立環境

  1. 為 C:\classictalk 目錄的 Jazz 構件開發建立一個環境。該位置在以下的子步驟中將會引用為 [InstallDir]。
    1. 執行 [InstallDir]\jazz\client\eclipse\TeamConcert.exe
    2. 選擇 Preferences > Plug-in Development > Target Platform,並點選 Add
    3. 選擇 File System,然後點選 Next
    4. 點選 Add,並選擇 [InstallDir]\jazz\server\license-update-site\plugins
    5. 點選 Finish
  2. 點選 Window > Open Perspective > Java
  3. 匯入 classictalk.zip 檔案,您可以從本文的 Downloads 部分中得到該文。
    1. 選擇 File > Import > Existing Projects into Workspace,然後點選 Next
    2. 點選 Select archive file,然後選擇 classictalk.zip
    3. 點選 Finish
  4. 向 ClassicTalkConnectorClient 專案新增 Java 客戶端庫:
    1. 下載 Jazz Plain Java Client Libraries,它位於 Web 上 Rational Team Concert 1.0.1 下載頁面 Incubators 之下。
    2. 右擊專案,並選擇 Build Path > Configure Build Path,然後選擇 Libraries 項。
    3. 按照“使專案聯結器就緒”中的“RTC 1.0”用例來新增 JAR 檔案(檢視 參考資料)。

重點:
不要忘了新增 org.eclipse.core.runtime_3.3.100.v20070530.jar 檔案,它沒有在 Rational Team Concert (RTC) 1.0 用例中列出。

設計 Jazz 儲存庫與服務

在這個部分中,您將會關注以下三種外掛專案:

  • com.example.classictalk.common
  • com.example.classictalk.services
  • com.example.classictalk.launches

這些專案與 Jazz 儲存庫,以及 圖 4 中的 RESTful 服務相關。

儲存模型

為將您自己的物件複製到 Jazz 儲存庫中,您必須為 Jazz 伺服器上儲存物件建立您自己的儲存庫。儲存模型允許外部性物件,以一種外部性物件可以對映到 Jazz 儲存庫資料庫的方式,來作為特定構件的資料儲存到 Jazz 儲存庫中,同時填充到物件中,然後與其他的資料交換格式一起使用。在實踐中,儲存模型只限定於核心模型,這意味著對於使用儲存模型有一些特定的習慣與規則(檢視 參考資料 中對於“JazzTalk 構件開發漫步”與“JazzBot 構件開發漫步”的連結)。儲存外部性物件的資料結構可以使用 EMF 核心模型來定義。在儲存模型中,準備好了三種型別的:Auditable、SimpleItem 與 Helper。

  • AuditableSimpleItem 都是 Jazz 專案。Auditable 保持了以前狀態的線性歷史(沒有分支),而 SimpleItem 則不能。
  • Helper 並不是一個 Jazz 專案,它屬於 Auditable 或者 SimpleItem。Helper 必須與它們中的一個一起儲存,而當相應的 Auditable 或者 SimpleItem 終止時 Helper 也會終止。

重點:
Item Connector 只能使用可審計的 ESuperType 來管理 Jazz 專案。這是因為 Item Connector 通過將 Jazz 專案的新狀態與過去的狀態相比較,來決定 Jazz 專案是否被改動過。這樣的 Jazz 專案應該包含有一個“識別符號”屬性,通過它來識別相應的外部性物件。圖 5 是 classictalk 核心模型的一個螢幕截圖。


圖 5. com.example.classictalk.common 中的 classictalk.ecore 項
顯示結構

Jazz 伺服器的服務

Item Manager 需要一種服務來儲存 Jazz 專案(圖 4 中的“儲存服務”)。通常來說,服務的介面是在普通包中定義的(例如,com.example.classictalk.common)。com.ibm.team.repository.common.components 擴充套件點用於配置介面。圖 6 顯示了 MANIFEST.MF 檔案中的 classictalk 介面,該檔案通過 Plug-in Manifest Editor 來開啟。


圖 6. com.example.classictalk.common 中的 MANIFEST.MF 檔案
Extensions 介面,強調顯示的構件項

服務本身是在服務包(com.example.classictalk.services project)中定義的。com.ibm.team.repository.service.serviceProvider 擴充套件點用於配置服務。圖 7 顯示了在 Plug-in Manifest 編輯器中開啟 MANIFEST.MF 檔案中 classictalk 服務的定義。


圖 7. com.example.classictalk.service 中的 MANIFEST.MF 檔案
Extensions 介面,強調顯示的 serviceProvider

外部性伺服器上的服務

外部性伺服器通常為 External Repository Manager 提供了至少四種服務:

  • 獲取所有的物件
  • 獲取一種物件
  • 建立一個物件
  • 更新一個物件

在一個典型的實施過程中,這些服務是很方便的,它們的名字分別是 getAllObjectsgetObjectpostObject 以及 putObject。外部性伺服器必須為處理外部性物件和 Jazz 伺服器或者客戶端提供這些可用的服務,以讓客戶端為訪問這些服務做好準備。

匯出一般的包

一般包被 Item Connector 客戶端所引用,也被 Jazz 伺服器所引用。您可以使用匯出嚮導來將包作為 Java Archive (JAR)檔案匯出,而匯出的 JAR 檔案在一般包被匯出之後,必須新增到 ClassicTalkConnectorClient 專案的 Referenced Libraries。圖 8 顯示了匯出嚮導,它將一般包作為 JAR 檔案匯出。


圖 8. Export 嚮導
"Deployable plug-ins and fragments" 介面

構建儲存庫資料庫

構建 Jazz 儲存庫的一種方式是使用 AllTestsCreateDB JUnit 測試,它包含在 com.ibm.team.repository.service.tests 外掛中(檢視 參考資料 中“構建一個 Jazz 儲存庫資料庫”中所列出的連結)。物理上,該儲存庫是在 -Dcom.ibm.team.repository.db.jdbc.location 選項中指定的資料夾中建立的。圖 9 顯示了 JUnit 外掛測試中所做配置的一個範例。

下面是構建 Jazz 儲存庫的步驟:

  1. 右擊 com.example.classictalk.launches > launches > create Jazz repository.launch 來選擇 runAs
  2. 點選 Create Jazz repository,如圖 9 所示。


圖 9. 建立 Jazz repository.launch
強調顯示的 "Run" 檢視,"Create Jazz repository"  

擴充套件專案聯結器的構件

在這個部分中,我們將會把注意力放在兩個專案上:ClassicTalkConnectorClient 與 com.example.classictalk.interop.managers 專案。這些專案對應於 圖 4 中的專案聯結器客戶端、Item Manager、Synchronization Rule 與 External Repository Manager。

專案聯結器客戶端

基本上,所有外部性的物件都由外部性服務的 getAllObjects 服務獲取,而每一個物件都根據前面所列出的七步來進行處理。ExternalProxy 專案有對外部性物件的連結,而該連結使用一個 URL 進行描述。在 Classic Talk Connector Client 專案中,我們將外部儲存庫的 URL 與作為外部性物件 ID 屬性名字及值混合體的 識別符號組合而成。

按照以下的步驟來建立一個 ClassicTalkConnectorClient 專案:

  1. 建立一個名為 ClassicTalkConnectorClient 的 Java 專案。
  2. 右擊專案並選擇 Build Path > Configure Build Path,然後點選 Libraries 項。
  3. 然後新增“匯出普通包”部分中匯出的 com.example.classictalk.common_1.0.0.jar 檔案。
  4. [InstallDir]\jazz\client\eclipse\plugins 資料夾新增 com.example.classictalk.common_1.0.0.jar 檔案。這就是同步化狀態編輯器中連結專案的資訊 (見於 圖 17)。
  5. 通過參考“專案聯結器構件”部分中“專案聯結器客戶端”章節的七個步驟來實施專案聯結器客戶端。

專案管理器

專案管理器用於建立並更新 Jazz 專案。為了實現這些功能,該構件應該實施諸如 createItemsaveStategetState 之類的方法,擴充套件 com.ibm.team.interop.service.AbstractInteropItemManager 類並從 com.ibm.team.interop.service.IInteropItemManager 中實施介面來實現以上的功能。這些方法的解釋與範例專案中的原始碼一起出現,您可以在本文的 Downloads 部分中找到它們。接下來描述 斜體 顯示的詞就是原始碼中實現變數的名字(見於下面列出的程式碼行 1,2,3)。

  • createItem: 方法通過訪問 com.ibm.team.repository.common.IItemType#createItem 方法來負責建立一個 Jazz 專案。
  • saveState: 方法將 newState 的屬性對映到 workingItem 專案,然後使用儲存服務來儲存專案(圖 4)。
  • getState: 方法根據 propertyNames 定義的金鑰列表來從 item 獲取金鑰/值對,並將它們作為一個對映物件來返回。


程式碼行 1. 建立專案方法

				
public IItem createItem(Map itemState, IProcessArea processArea,
    Map preferences) {
  return ChatThread.ITEM_TYPE.createItem();
}


程式碼行 2. saveState 方法
				
public IItem saveState(IItem workingItem, Map newState,
    IProcessArea processArea, Map preferences)
    throws TeamRepositoryException {
  ChatThread workingChatThread = (ChatThread)workingItem;
  
  for(Map.Entry entry : newState.entrySet()) {
    setProperty(entry.getKey(), entry.getValue(), workingChatThread);
  }
  return getClassicTalkService().saveChatThread(workingChatThread);
}


程式碼行 3. 獲得狀態方法
				
public Map getState(IItem item, List propertyNames,
    Map preferences) throws TeamRepositoryException {
  ChatThread chatThread = (ChatThread) item;
  Map state = new HashMap();
  Iterator iter = propertyNames.iterator();
  while(iter.hasNext()){
    String propName = iter.next();
    state.put(propName, getProperty(propName, chatThread));
  }
    return state;
}

另一個重要的方法是 getSupportedTypeInfo,它定義了同步化規則的 專案型別專案屬性圖 11)。專案型別 就是與該同步化規則相同步化的 Jazz 專案,並且該名字通過訪問 ITypeInfoDTO 物件的 setName 方法來設定。Item Property 是與該同步化規則同步化的 Jazz 專案的屬性,而且這些屬性通過訪問 IPropertyInfoDTO 物件的 add 方法來設定(見於程式碼行 4)。


程式碼行 4. 得到支援的 TypeInfo 方法

				
private static ITypeInfoDTO[] fgTypeInfo;

//Item Property
public static final String ID_PROPERTY = "id";
public static final String TEXT_PROPERTY = "text";
private static final String[] PROPERTIES = {
  ID_PROPERTY,
  TEXT_PROPERTY,
};

public ITypeInfoDTO[] getSupportedTypeInfo(IProcessArea processArea) {
  if(fgTypeInfo == null){
    fgTypeInfo = new ITypeInfoDTO[1];
    fgTypeInfo[0] = InteropFactory.INSTANCE.createTypeInfoDTO();
    
    //Item type
    fgTypeInfo[0].setName(ChatThread.ITEM_TYPE.getNamespaceURI() +
        "." + ChatThread.ITEM_TYPE.getName());
    List propertyInfo = fgTypeInfo[0].getProperties();
    
    for(String name : PROPERTIES){
      IPropertyInfoDTO info = InteropFactory.INSTANCE.createPropertyInfoDTO();
      info.setName(name);
      info.setIdentifier(name.equals(ID_PROPERTY));
      propertyInfo.add(info);
    }
  }
  return fgTypeInfo;
}
  

按照以下的步驟來建立一個 com.example.classictalk.interop.managers 專案:

  1. 建立一個名為 com.example.classictalk.interop.managers 的外掛專案(圖 10)。
  2. 點選 MANIFEST.MF 檔案中的 Dependencies 項,並新增這些需要的外掛:
    • com.ibm.team.interop.service
    • com.ibm.team.interop.common
    • com.ibm.team.process.common
    • com.ibm.team.repository.common
    • com.ibm.team.repository.service
    • com.example.classictalk.common
  3. 點選 MANIFEST.MF 檔案中的 Extensions 項,並新增以下的擴充套件項(見於圖 10):
    • 擴充套件點: com.ibm.team.interop.service.itemManager
    • 專案管理器:
      • ID: com.example.classictalk.interop.managers.ClassicTalkItemManager
      • 名字:ClassicTalkItemManager
    • 擴充套件服務:
      • 構件 Id: com.example.classictalk.interop
      • 實施類: com.example.classictalk.interop.managers.ClassicTalkItemManager
    • 前提條件:
      • 介面: com.example.classictalk.common.IClassicTalkService
  4. 實施 createItemsaveStategetStategetSupportedTypeInfo 方法。


圖 10. Item Manager 與 External Repository Manager 的擴充套件
強調顯示的 Extensions 檢視,Item Manager 擴充套件項

External Repository Manager

External Repository Manager 就是您用來建立、獲取並更新外部性物件的工具。為了提供這些功能,該構件實施了一些方法:createObject、getState、updateState 以及 findObject。它們都是通過擴充套件 com.ibm.team.interop.service.AbstractInteropExternalManager 類及實施介面 com.ibm.team.interop.service.IInteropExternalManager 來得以實施的。這些方法的解釋應該基於範例專案中 External Repository Manager 的原始碼(見於 Downloads)。以下描述的斜體詞就是原始碼例項化變數的名字(也見於程式碼行 5、6、7 和 8)。

  • createObject:這種方法根據金鑰列表 propertyNamesstate 對映物件中獲得金鑰/值對,並使用 postObject 服務在外部性儲存庫中建立一個外部性的物件(圖 4)。像使用者 ID、密碼、儲存庫 URI 這樣的連線資訊,會為 externalConnection 定義。postObject 服務會返回新建立物件的狀態資料,當由於外部性儲存庫儲存操作可能存在的邊效應而訪問服務時,它可能與使用的 狀態 資料不一樣。返回的資料應該得到剖析並儲存在 returnedState 對映物件中。最終,該方法會使用外部物件 ID 屬性的名字與值的混合項來建立一個 java.net.URI 物件,然後使用 URL 物件來訪問 com.ibm.team.interop.service.internal.IInteropExternalManager#CreationInfo 方法,並返回它的結果。
  • getState: 該方法會找到一個外部性物件,該物件的標示符會定義為一個 uri URI 物件,並得到物件的值,其相應的金鑰在 propertyNames 中指定。這是通過訪問外部性儲存庫中的 getObject 服務來實現的。聯絡資訊在 externalConnection 中得到了定義。最終,金鑰與值會放到一個對映物件中並返回。
  • updateState: 該方法找到了一個標示符定義為 uri URI 物件的外部性物件,並使用外部性儲存庫中的 putObject 服務來更新金鑰為 propertyNames 的值。更新的值可以使用 propertyNames 的金鑰列表來從 newState 處獲得更新的值。然後該方法會分析來自 propertyNames 服務的返回資料,然後將金鑰/值屬性置於 returnedState 所定義的對映物件中,因為新建立物件中的返回資料由於外部性儲存庫中儲存操作的邊界效應,可能不同於 newState。如果 newStatereturnedState 的值返回了相似或者其他值的話,該方法會返回 true。
  • findObject: 這種方法使用 getObject 服務,來使用 idPropName 金鑰及其來自 state 對映的值來找到外部性的物件,如果沒有找到外部性的物件的話,就使用 propertyNames 金鑰剖析返回的資料以設定金鑰/值對。最後,該方法會建立並返回標示符的金鑰/值的 URL 物件。聯絡的資訊儲存在 externalConnection 中。


程式碼行 5. createObject 方法

				
public CreationInfo createObject(String typeName, Map state,
    Map returnedState, List propertyNames,
    IExternalRepositoryConnection externalConnection,
    IProcessArea processArea) {
  String repositoryUri = externalConnection.getName();
  String userId = externalConnection.getUserId();
  String password = externalConnection.getPassword();
  
  Map castState = new HashMap();
  for(int index = 0; index < propertyNames.size(); index++){
    String propertyName = propertyNames.get(index);
    castState.put(propertyName, (String)state.get(propertyName));
  }
  
  Document doc = RestClient.doMethod(repositoryUri, path, clazz,
	    userId, password, castState, RestClient.METHOD_POST);
  List> extProps = RestClient.parseDocument(doc,
	    xpath, propertyNames);
  
  CreationInfo creationInfo = null;
  if(extProps.size() == 1){
    Map extProp = extProps.get(0);
    String extIdValue = extProp.get(EXT_ID_PROPERTY);
    String urlStr = repositoryUri + "/" + EXT_ID_PROPERTY + "=" + extIdValue;
    try {
      URI uri = new URL(urlStr).toURI();
      creationInfo = new CreationInfo(uri);
      for(int index = 0; index < propertyNames.size(); index++){
        String propertyName = propertyNames.get(index);
        returnedState.put(propertyName, extProp.get(propertyName));
      }
    } catch (MalformedURLException e) {
      e.printStackTrace();
    } catch (URISyntaxException e) {
      e.printStackTrace();
    }
  }
  return creationInfo;
}
  


程式碼行 6. getState 方法
				
public Map getState(URI uri, List propertyNames,
    IExternalRepositoryConnection externalConnection) {
  String repositoryUri = externalConnection.getName();
  String userId = externalConnection.getUserId();
  String password = externalConnection.getPassword();
  
  Map params = extractIdMapFromExtURI(uri);
  
  for(int index = 0; index < propertyNames.size(); index++){
    String propertyName = propertyNames.get(index);
    if(params.containsKey(propertyName))continue;
    params.put(propertyName, "");
  }
  
  Document doc = RestClient.doMethod(repositoryUri, path, clazz,
	    userId, password, params, RestClient.METHOD_GET);
  List> extProps = RestClient.parseDocument(doc,
	    xpath, propertyNames);
  
  if(extProps.size() == 1){
    return extProps.get(0);
  }else{
    return null;
  }
}


程式碼行 7. updateState 方法
				
public boolean updateState(String typeName, URI uri,
  Map newState, Map lastState,
  Map returnedState, List propertyNames,
  IExternalRepositoryConnection externalConnection,
  IProcessArea processArea) {
  String repositoryUri = externalConnection.getName();
  String userId = externalConnection.getUserId();
  String password = externalConnection.getPassword();
    
  Map params = extractIdMapFromExtURI(uri);
  for(int index = 0; index < propertyNames.size(); index++){
    String propertyName = propertyNames.get(index);
    if(params.containsKey(propertyName))continue;
    if(newState.containsKey(propertyName)){
      params.put(propertyName, (String)newState.get(propertyName));
    }
  }
  Document doc = RestClient.doMethod(repositoryUri, path, clazz,
	    userId, password, params, RestClient.METHOD_PUT);
  List> extProps = RestClient.parseDocument(doc,
	    xpath, propertyNames);
  
  boolean returnedFlag = false;
  if(extProps.size() == 1){
    Map extProp = extProps.get(0);
    if(params.get(EXT_ID_PROPERTY).equals(extProp.get(EXT_ID_PROPERTY)) &&
      params.get(EXT_TEXT_PROPERTY).equals(extProp.get(EXT_TEXT_PROPERTY))){
      returnedFlag = true;
    }
    
    returnedState.put(EXT_ID_PROPERTY, extProp.get(EXT_ID_PROPERTY));
    returnedState.put(EXT_TEXT_PROPERTY, extProp.get(EXT_TEXT_PROPERTY));
  }
  return returnedFlag;
}
  


程式碼行 8. findObject 方法
				
public URI findObject(String typeName, String idPropName,
    Map state, Map returnedState,
    List propertyNames,
    IExternalRepositoryConnection externalConnection) {
  
  String repositoryUri = externalConnection.getName();
  String userId = externalConnection.getUserId();
  String password = externalConnection.getPassword();
  
  Map params = new HashMap();
  params.put(idPropName, (String)state.get(idPropName));
  Document doc = RestClient.doMethod(repositoryUri, path, clazz,
	    userId, password, params, RestClient.METHOD_GET);
  List> extProps = RestClient.parseDocument(doc,
	    xpath, propertyNames);
  URI uri = null;
  if(extProps.size() == 1){
    Map extProp = extProps.get(0);
    String extIdValue = extProp.get(EXT_ID_PROPERTY);
    String urlStr = repositoryUri + "/" + EXT_ID_PROPERTY + "=" + extIdValue;
    try {
      uri = new URL(urlStr).toURI();
    } catch (MalformedURLException e) {
      e.printStackTrace();
    } catch (URISyntaxException e) {
      e.printStackTrace();
    }
    returnedState.put(EXT_ID_PROPERTY, extProp.get(EXT_ID_PROPERTY));
    returnedState.put(EXT_TEXT_PROPERTY, extProp.get(EXT_TEXT_PROPERTY));
  }
  return uri;
}


另一種重要的方法是 getSupportedTypeInfo 方法,它為同步化規則定義了 External Type 與 External Property (圖 11)。External Type 是該同步化規則中要同步化的外部性物件的型別名,而且其名字是由 ITypeInfoDTO 物件的 setName 方法來設定的。External Property 是與該同步化規則中的外部性物件一起同步化,而且這些屬性可以使用 IPropertyInfoDTO 物件的 add 方法來進行設定(見於程式碼行 9)。


程式碼行 9. getSupportedTypeInfo 方法

				
private static ITypeInfoDTO[] fgTypeInfo;

//External Property
private static final String EXT_ID_PROPERTY = "id";
private static final String EXT_TEXT_PROPERTY = "text";
private static final String[] PROPERTIES = {
   EXT_ID_PROPERTY,
   EXT_TEXT_PROPERTY,
};

public ITypeInfoDTO[] getSupportedTypeInfo(
    IExternalRepositoryConnection externalConnection) {
  if (fgTypeInfo == null) {
    fgTypeInfo = new ITypeInfoDTO[1];
    fgTypeInfo[0] = InteropFactory.INSTANCE.createTypeInfoDTO();
    
    //External type
    fgTypeInfo[0].setName(ChatThread.ITEM_TYPE.getNamespaceURI() +
     "." + ChatThread.ITEM_TYPE.getName());
    List propertyInfo = fgTypeInfo[0].getProperties();
    
    for(String name : PROPERTIES) {
      IPropertyInfoDTO info = InteropFactory.INSTANCE.createPropertyInfoDTO();
      info.setName(name);
      info.setIdentifier(name.equals(EXT_ID_PROPERTY));
      propertyInfo.add(info);
    }
  }
  return fgTypeInfo;
}
  

下面是建立 com.example.classictalk.interop.managers 專案的步驟:

  1. 選擇一個已存在的專案。例如,選擇 com.example.classictalk.interop.managers
  2. 點選 MANIFEST.MF 檔案中的 Extensions 項,並新增這些擴充套件(圖 10):
    • 擴充套件點:com.ibm.team.interop.service.externalManager
    • 外部性管理器:
      • ID:com.example.classictalk.interop.managers.ClassicTalkExternalManager
      • 名字:ClassicTalkExternalManager
    • 擴充套件服務:
      • 構件 Id: com.example.classictalk.interop
      • 實施類: com.example.classictalk.interop.managers.ClassicTalkExternalManager
  3. 實施 createObject, getState, updateStategetSupportedTypeInfo 方法。

同步化規則

同步化規則將外部性資料對映到 Jazz 儲存庫屬性名。為了提供這種功能,同步化規則應該選擇 Item Manager 和 External Repository Manager,並引用 Item Manager 和 External Repository Manager 中的 getSupportedTypeInfo 方法定義的屬性,來將外部性屬性名對映到 Jazz 屬性名。您可以使用同步化規則編輯器來編輯這些規則。它們都儲存在專案區域中(圖 11)。這需要伺服器(例如,Jetty)已經處於執行狀態,而且專案區域已經建立完畢。


圖 11. 編輯同步化規則
同步化規則介面  

執行用例

為聯結器列出了四種用例,以將一個外部性儲存庫和 Jazz 儲存庫進行同步化。在這些用例中,伺服器中資料的建立與更新操作可以通過權衡 postObject 與 putObject 服務的實現來完成。通過在瀏覽器上使用 getAllObjects 服務,您可以輕鬆檢查物件是否成功匯入了。在“classictalk”範例中(通過 下載 部分獲得),外部性伺服器或者 Jazz 伺服器分別在同一臺機器上的埠 7443 或者 9443 處執行,同時 Jazz 客戶端也是在相同的機器上執行的。這是一種用於簡化的範例環境。

設定配置

警告

您在一個真實的商業環境中執行一個系統之前,您必須配置 Java 2 平臺、Enterprise Edition 認證。這個值決定了 J2EE 認證機制是否處於啟用狀態。該值設定為真,用於節省開發環境中 J2EE 認證配置的時間。啟用 J2EE 認證的操作超出了本文的討論範圍。
  1. 向 [InstallDir]\jazz\server 資料夾中的 teamserver.properties 新增以下的引數 (重點: 檢視工具欄):
    com.ibm.team.repository.ws.allow.identity.assertion = true
  2. 啟用外出性同步化並設定時間跨度。
    1. 向 [InstallDir]\jazz\server 資料夾中的 teamserver.properties 屬性新增以下的引數:
      • com.ibm.team.interop.OutgoingSyncTask.fixedDelay=30
        同步化時間跨度(秒)
      • com.ibm.team.interop.service.outgoingSyncEnabled=true
        外出性同步化是否處於啟用狀態
    2. 在 Jazz 伺服器執行之後,您可以使用 ADMIN 作為使用者 ID 與密碼,來訪問 https://localhost:9443/jazz/admin,並選擇 Server > Advanced Properties > Item Interoperation 以編輯以下的引數(見於圖 12):
      • Outgoing Synchronization Task Fixed Delay:(按照您的喜歡任意設定)
      • Outgoing Synchronization Enabled: true
  3. 向 [InstallDir]\jazz\server 資料夾中的 log4j.properties 為 Item Connector 的日誌新增以下的引數:
    • log4j.logger.com.ibm.team.interop=DEBUG
    • log4j.logger.com.ibm.team.interop.service.outgoingSyncJob=
      DEBUG


圖 12. 使用一個 Web 瀏覽器來編輯引數
 "True" 與 "30" 介面

建立專案區域

  1. 右擊 com.example.classictalk.launches > launches > run Jazz server.launch 來選擇 runAs,然後點選 run Jazz server
  2. 點選 Window > Open Perspective > Work item
  3. 點選 Create a Repository Connection 然後新增以下的條目:
    • URI: https://localhost:9443/jazz
    • 使用者 ID: ADMIN
    • 密碼: ADMIN
  4. 右擊儲存庫連線並選擇 New > user,然後建立一個使用者。
    1. 點選 No 以回到“匯入使用者?”對話方塊。
    2. 輸入以下的使用者資訊:
      • 名字: jazzuser
      • 使用者 ID: jazzuser
      • 郵件地址: (您所喜歡的)
      • 客戶訪問許可證: Rational Team Concert - Developer
    3. 點選 Save
  5. 刪除以前的儲存庫連線,然後建立新的連線:
    • URI: https://localhost:9443/jazz
    • 使用者 ID: jazzuser
    • 密碼: password
  6. 右擊新儲存庫連線並點選 New > Project Area
    1. 輸入專案名和總結 :
      • 名: JazzProject
      • 總結 (根據您的喜好自由設定)
    2. 點選 Deploy Templates
    3. 選擇 Agile Process,然後點選 Finish

編輯外部儲存庫連線與同步化規則

  1. 如果 Jazz 伺服器尚未執行的話就執行它。
    • 右擊 com.example.classictalk.launches > launches > run Jazz server.launch 來選擇 runAs
  2. 選擇 Window > Show view > other > Team > Synchronization Rules 來開啟同步化檢視,然後右擊帶有以下三個引數的 External Repository Connections 來選擇 New > External Repository Connections (圖 13):
    • 名字: http://localhost:7080/jazz
    • 連線資訊:(根據您的喜好自由設定)
    • 使用者 ID: extuser
    • 密碼: password
    • 專案區域: JazzProject
      注意:
      這些引數可以通過 External Repository Manager 中的 IExternalRepositoryConnection 物件來得到引用。
    • 取消 Disable automatic outgoing synchronization 的選擇。
  3. 在同步化檢視中,右擊 JazzProject,並選擇 New > Synchronization Rule 來建立同步化規則(檢視 圖 11
    1. 選擇 Item Manager 與 External Repository Manager:
      • 名字: My ClassicTalk Manager Rule (根據您的喜好自由設定)
      • 專案型別:ChatThread – com.example.classictalk
      • 專案管理器:ClassicTalkItemManager
      • 外部儲存庫:http://localhost:7080/jazz
      • 外部性管理器:ClassicTalkExternalManager
      • 外部性型別: ChatThread – com.example.classictalk
    2. 點選 Property Mappings 中的 initialize,然後點選 Save
      • 分別根據 Item 或者 External Repository Manager 來設定 Item 或者 External 屬性。
  4. 關閉 Jazz 伺服器。


圖 13. 編輯外部儲存庫連線
"Create External Repository Connection"介面

建立外部性伺服器

  1. 右擊 com.example.classictalk.launches > launches > create External repository.launch 來選擇 runAs
  2. 右擊 com.example.classictalk.launches > launches > run External server.launch 檔案來選擇 runAs
  3. 點選 Window > Open Perspective > Work item
  4. 右擊 Repository Connections,選擇 New > Jazz Repository Connection,然後新增以下的條目:
    • URI: https://localhost:7443/jazz
    • 使用者 ID: ADMIN
    • 密碼: ADMIN
      注意:
      當您在訪問 https://localhost:9443/jazz 時會出現一個出錯資訊,您只管忽略它。這是因為“建立 Jazz 伺服器”處的儲存庫連線會試著訪問已經關閉的 Jazz 伺服器。
  5. 右擊儲存庫連線來選擇 New > user,然後建立一個使用者:
    1. 點選 No 以回應“匯入使用者?”對話方塊。
    2. 輸入使用者資訊:
      • 名字: extuser
      • 使用者 ID: extuser
      • 郵件地址 (根據您的喜好自由設定)
      • 客戶訪問許可證: Rational Team Concert - Developer
    3. 點選 Save
  6. 刪除以前的儲存庫連線,然後再一次建立新的連線:
    • URI: https://localhost:7443/jazz
    • 使用者 ID: extuser
    • 密碼: password
  7. 右擊新儲存庫連線來點選 New > Project Area
    1. 輸入專案名和總結 :
      • 名字: ExternalProject
      • 總結:(根據您的喜好自由設定)
    2. 點選 Deploy Templates
    3. 選擇 Agile Process,然後點選 Finish

用例

對於專案聯結器有四種典型的用例。為了執行這些用例,您需要讓外部性伺服器與 Jazz 伺服器已經處於執行狀態。讓我們選擇 com.example.classictalk.launches > launches > use case 1.1.launch,然後 執行 Item Connector Client.launch,來執行用例 1.1。圖 14 顯示了 Jazz 伺服器上的 Jazz 專案,外部性物件會匯入到 Jazz 伺服器中,您可以使用一個 Web 瀏覽器並使用以下的 URL 來檢視這種操作:http://localhost:9080/jazz/service/com.example.classictalk.common.IClassicTalkService/allChatThreads。

  1. 建立一個新的物件:
    • 外部儲存庫中新建立的物件匯入到 Jazz 儲存庫中。
    • Jazz 儲存庫中新建立的資料匯出到外部儲存庫中。
  2. 更新一個已經存在的物件:
    • 更新 Jazz 儲存庫中外部儲存庫更新資料中的資料。
    • 更新外部儲存庫中 Jazz 儲存庫更新資料中的資料。


圖 14. 帶有匯入外部性物件的 Jazz 伺服器的 Jazz 專案
XML 格式的 Jazz 專案

進入-外出衝突

當您在更新一個已存在的物件時,會發生兩種型別的衝突。第一種是資料得到同步化之後,當外部儲存庫中一個物件更新的屬性傳送到 Jazz 儲存庫中更新的相應 Jazz 專案時,會發生進入性衝突(圖 15)。為了避免進入性衝突,那麼外部性物件的更新屬性應該在更新 Jazz 專案之前使用專案聯結器客戶端來進行同步化。


圖 15. 一個進入性衝突外部性物件與 Jazz 專案的時間限制
進入性衝突的工作流程

第二種衝突是外出性衝突,當資料得到同步化後,在 Jazz 儲存庫中某個 Jazz 儲存庫中的更新屬性傳送到外部儲存庫中相應的更新物件時,就會發生這種衝突(圖 16)。為了避免外出性衝突,那麼 Jazz 專案的更新屬性應該在更新外部性物件之前使用外部儲存庫管理器來進行同步化。


圖 16. 外出性衝突中外部性物件與 Jazz 專案的時間限制
外出性衝突的工作流程

圖 17 顯示了一個進入性衝突,如 Synchronization Status 編輯器中所示。底部的兩個按鈕允許您去決定什麼資料是有效的(圖 17 中的紅色圓圈)。如果您點選 Accept External State,那麼 Jazz 專案的屬性就會被外部性物件的屬性所覆蓋。如果您點選的是 Accept Item State,那麼外部性物件的屬性會被 Jazz 專案的屬性所覆蓋(圖 18)。外部性衝突的解決方式與之類似(圖 19)。


圖 17. Synchronization Status 編輯器檢視中的一個進入性衝突
選項:接受外部性或者專案狀態


圖 18. 通過選擇“Accept Item State”來解決衝突
外部性值:編輯的 Jazz 專案


圖 19. 同步化狀態編輯器的一個輸出性的衝突
輸出性衝突的選項

如果您想要得到關於衝突解決的更多資訊,那麼您可以檢視列於 參考資料 中“建立一個新的專案聯結器”部分中“其他的考慮”。

總結

我們解釋了怎樣使用 Rational Team Concert 軟體中的專案聯結器來設計和建立新的聯結器。外部性與 Jazz 儲存庫可以輕鬆地通過三步來完成。為了決定聯結器的總體結構,第一步是選擇一種開發模式,在這個開發模式下專案聯結器客戶端執行。為了開發 Jazz 儲存庫與服務,第二步是使用一個核心模型,並在 Jazz 團隊伺服器上的擴充套件點註冊服務。為了權衡使用專案聯結器,第三步是實現專案聯結器的每一個構件。聯結器可以輕鬆支援每一個部門系統之間的資料同步化。

致謝

作者感謝 John Vasta 提供的技術性支援,以及 Kinichi Mitsui 和 Takehiko Amano 對本文草稿所做的評審工作。

原文連結:http://www.ibm.com/developerworks/cn/rational/10/synchronizedatarepositoriesbyusingtherationalteamconcertitemconnector/index.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14780914/viewspace-670101/,如需轉載,請註明出處,否則將追究法律責任。

相關文章