基於 Lotus Expeditor 6.1 的複合應用開發

2007 年 7 月 27 日

面對市場激烈的競爭,企業需要進行快速響應,以滿足市場的需求,複合應用是提高企業業務的靈活性的一個有效的解決方案, 使企業可以充分利用和擴展現在的資源,節約成本, 最大限度地複用組件。 本文主要討論複合應用的概念以及如何開發基於 Lotus Expeditor 的複合應用,通過一個 Eclipse 實例,展示了具有鬆散耦合的複合應用的開發過程。

引言

複合應用是面向服務架構(SOA)中的關鍵要素,它能爲企業提供業務靈活性的同時,能夠更容易創建新服務或者將現有的IT資產轉化爲可重用的服務,這些服務可以快速適應不斷變化的業務需求。複合應用是由多個相互通訊相互操作的組件組成,而這些組件是鬆散耦合的,並且在其它複合應用中重複使用。一個組件中的數據發生了變化,可以通過消息廣播通知給其它組件,與之線接(Wire)的組件收到消息後進行相應的處理以反映到組件中數據的變化。

本文主要討論複合應用的概念以及如何開發基於 Expeditor 的複合應用,通過一個 Eclipse 實例,展示了具有鬆散耦合的複合應用的開發過程。





回頁首


Lotus Expeditor 客戶端平臺

Lotus Expeditor 6.1 是全新的 IBM 智能客戶端平臺軟件,它提供的客戶端可以通過服務器進行管理,可以遠程部署和維護,它的客戶端是多種應用集成的複合界面,支持離線應用等。

複合桌面

可以將部署後的不同的應用組件集成在一個可視的窗體裏,而且可以集成後臺的業務流程應用。用戶不必在不同應用之間進行來回切換,還可以集成工作流在一個複合應用裏,複合應用的組件可以是 Swing、文字終端、Visual Basic、Active X、本地或 Web 的 Applet 如 AJAX、PDF、JSP 和 Forms。Lotus Expeditor 還提供了這些組件之間互相通訊,相互操作的能力。不僅可以使得一個應用作爲複合桌面的一個組件,而且通過其它新的應用增強現有應用的功能。利用 Lotus Expeditor,Portal 可以是複合應用的一個組成部分,現有的應用可以經過簡單的封裝,作爲複合桌面中的一個組件,最終用戶在不影響其操作習慣的基礎上,使用集成的複合桌面。這樣可以將 Lotus Expeditor 貫穿所有的業務,從而降低最終用戶的重新培訓的成本,提高生產效率。

集成

Lotus Expeditor 可以將不同數據源的信息集中展現在用戶眼前,這些信息可以通過 Portal 應用、Workplace Form 應用、關係數據、Web 服務等形式展現。Lotus Expeditor 爲 Portal 提供了一個複合桌面,將 Portal 應用通過響應快速、界面豐富的桌面應用展現出來,而 Portal 的拓撲結構在客戶端維護,通過視圖的形式展現 Protlet 以及相互間的通訊,而且 Portal 應用還可用於離線模式,即使網絡不連通、不穩定的情況下,仍能高效的工作,這特別適用於移動辦公的職員和外勤人員。

Lotus Expeditor 可以使 Workplace Forms 應用變成離線應用,表單以及表單數據可存儲在桌面客戶端,當網絡連通後,可以和 Form 服務器進行同步。有些表單,特別是複雜表單,用戶可以離線填寫,例如在家裏填完後,保存在本地,當用戶有網絡時,再向服務器提交,這可極大的提高辦公效率。對於關係數據,Lotus Expeditor 可以使客戶不必重寫同步算法就可以使後臺 ODBC 或 JDBC 數據源,通過 DB2e 的穩健的同步能力,將後臺關係數據與客戶端數據進行同步。Lotus Expeditor 的客戶端服務能力使得從客戶端從接受 Web 服務更加容易,從而可以將後端應用如 Siebal 等將 Web 服務接口擴展到可管理客戶端環境。另外,Lotus Expeditor 可以利用 WBI 適配器來通過企業服務總線將不同的後端應用擴展到前端。

服務器端的中心化管理

Lotus Expeditor 可以支持從服務器端的遠程部署和維護,這樣可大大節約部署和維護的成本,應用管理器能自動完成諸如配置、分發、升級軟件、初始化下載等任務,還可以控制組件的訪問權限,對管理員和設備用戶區別對待。另外,爲了提高開發效率,Lotus Expeditor 提供了不同編程模型的選擇,開發人員可以開發基於 Eclispe 的用戶界面,也可以使用 Servlet 容器,這樣開發人員不管是寫基於 Eclipse 的應用,還是寫 Web 應用,都可以在客戶端運行。

支持離線

Lotus Expeditor 支持離線存儲的應用,當數據或者應用在在線時可以下載或同步,當設備離線時,用戶仍然可以處理事務,並將數據保存到本地,當網絡連通時,數據可按應用的邏輯同步到服務器,與服務器的通訊可以是同步,也可以是異步。

安全性

在安全性方面,Lotus Expeditor 提供了諸如加密、憑證存儲、鎖止桌面等功能來保證客戶端的安全性, 本地的憑證存儲可以使得通過本地即可驗證用戶,而不需連通網絡,這也使得本地客戶端的多個應用通過 Lotus Expeditor 客戶端實現單點登錄。桌面鎖止功能可防止用戶從客戶端安裝非授權的軟件,這樣,本機上只有服務器管理員安裝的軟件才能安裝於最終用戶的機器上。除此之外,Lotus Expeditor 支持多項加密技術(如:關係數據的加密)。





回頁首


複合應用

Lotus Expeditor 提供了將不同應用進行組裝的能力,這些應用雖然具有各自的功能,但通過組裝,它們之間可以相互通訊相互操作,進而使得組裝後的應用可看作一個單一的應用,從而在每個應用的功能之上增加了組合功能。複合應用有如下的特點:

  • 組件之間可以相互通訊;
  • 組件之間鬆耦合,可靈活地用於 SOA 架構;
  • 組件或資源可以複用;
  • 不同組件可以組裝在一個客戶端窗體中以獲得更加豐富的用戶體驗;
  • 統一的界面風格;
  • 事務型組件其信息取自不同的信息源;
  • 支持諸如驗證、角色和數據保密等安全性;

組件

構成複合應用的相互之間可以通訊和操作的應用程序,我們稱爲組件,這些組件可以動態進行組裝和拆卸,組裝後雖然他們可以進行相互通訊相互操作,但它們之間仍舊是鬆耦合的,一旦一個或多個組件移除,並不影響剩餘組件的功能。

不同的技術創建的視窗可以組裝於一個窗體內,這些視窗可看着作爲組件的外部展現,通過複合應用,可以將這些視窗集成起來,構成複合應用。可以用於組裝 Expeditor 複合應用的組件有如下類型:

  • SWT 組件;
  • AWT 組件;
  • 內嵌瀏覽器組件;
  • 支持 JSR168 標準的 Portlet 查看器組件;
  • 顯示 Servlet 及 JSP 的 Web 容器;
  • 可在本地顯示遠程 Portlet 的 WSRP 查看器;

Property Broker

構成一個複合應用的組件之間需要進行相互通訊,但由於這些組件可以是由不同的開發商提供,這就需要一個標準規範,用於指導開發人員開發出可以用於複合應用的組件。在 Expeditor 中,通過 Property Broker 提供了不同組件之間進行通訊的能力,組件的開發者可以通過 XML 配置文檔發佈組件的數據或屬性(Property)、消息的動作(Action)以及組件間的線接(Wire),Property Broker 能夠動態的檢測到已經變化的組件的數據,並查找註冊過的與該組件相連接或線接的其它組件中的動作,如果找到相應的動作,Property Broker 觸發該動作使之執行,這樣就完成了兩個鬆耦合組件間的通訊及操作。

在這些相互協作的組件中,提供數據的組件我們稱之爲提供者,而接受數據並進行動作的組件我們稱爲接受者,提供者的數據我們稱爲輸出數據,接受者的接受的數據我們稱爲輸入數據,如圖 1。


圖 1. 數據提供者組件的輸出數據和數據接受者組件的輸入數據
圖 1. 數據提供者組件的輸出數據和數據接受者組件的輸入數據

僅僅定義組件的輸出數據或輸入數據,兩個組件還不能進行相互通訊,只有通過 Property Broker 線接(Wire)起來的組件,才能進行相互通訊相互操作。線接的一端是數據提供者組件中的輸出數據,另一端是數據接受者的輸入數據,兩者的數據類型必須一致,如圖 2。


圖 2. 兩個組件的線接(Wire)
圖 2. 兩個組件的線接(Wire)

無論是組件發佈還是註冊,Property Broker 是通過一種 XML 文檔來完成的,這一文檔的格式是借自 Web 服務描述語言 WSDL(Web Services Description Language)格式,通過這個描述語言,可以描述在複合應用中組件的輸入輸出數據(property)、消息動作(action)以及類型等。





回頁首


Eclipse 組件樣例

本節我們給出一個由純粹 Eclipse 組件構成的複合應用,其功能是將當前北京時間換算爲世界其它時間,通過此樣例,可以詳細瞭解基於 Lotus Expeditor 客戶端的複合應用的構建過程。

此樣例由兩個組件組成,com.ibm.rcp.samples.cityselection 是用戶選取世界城市,並顯示當前的北京時間(當前系統時間),另外一個組件 com.ibm.rcp.samples.citytime 是顯示選中城市的當前當地時間。如圖 3:


圖 3. 實例界面:世界城市時間與北京時間換算
圖 3. 實例界面:世界城市時間與北京時間換算

消息的觸發是由 com.ibm.rcp.samples.cityselection 中,用戶點擊 Send 後,組件 com.ibm.rcp.samples.cityselection 通過 Property Broker 向組件 com.ibm.rcp.samples.citytime 廣播所選擇的城市名字,組件 com.ibm.rcp.samples.citytime 收到消息事件後,啓動一個動作,以計算並顯示當前該城市的當地時間。

消息動作及屬性的定義

通過 Property Broker,我們可以聲明組件的屬性(property)、動作(action)以及線接(wire),以便當鬆散耦合的某個組件的數據發生變化時,與之線接的組件獲得變化的數據並觸發相應的動作。 Expeditor 的 Property Broker 提供了一套機制,僅僅通過配置即可將消息動作及屬性進行定義,並自動註冊到 Property Broker。這種做法的好處是,開發人員可以不必瞭解 Property Broker 的 API,甚至不必需要 Property Broker 的知識。

Expeditor 客戶端 Property Broker 允許有不同類型的組件與之協同工作,當前 Expeditor 有三類動作處理程序:SWT_ACTIONPORTLETAWT_ACTION,開發人員可以根據需求實現這三種動作的一種,Broker 通過動作類型將變化了的數據傳遞給相應的動作處理程序。

如果一個組件沒有 UI 界面,可以通過 org.eclipse.core.commands.IHandler 接口來實現自己的動作,這時動作類型爲 COMMAND。如果組件依賴於 Eclipse 的界面,如 SWT 視圖,可以用 org.eclipse.core.commands.IHandler 接口來實現動作,並且是 SWT_ACTION 類型,也可以繼承 org.eclipse.jface.action.Action 來實現動作。用 SWT_ACTION 類型的好處是當組件所在的透視圖隱藏時,複合應用 XML 定義的線接(wire)就會失效。

下面我們討論如何在組件 com.ibm.rcp.samples.cityselection 中創建一個動作。首先將插件依賴於 com.ibm.rcp.propertybrokercom.ibm.rcp.propertybroker.swt, 然後創建一個 com.ibm.rcp.propertybroker.PropertyBrokerDefinitions 的擴展,這一擴展可自動註冊動作以及對應的輸入輸出數據。Lotus Expeditor Toolkit 提供了一個工具爲 Property Broker 定義模板,利用這一導航式工具,可定義出擴展、WSDL 文件及動作的 Java 類文件。在 Property Broker 定義的模板中,可選取模板 SWT Handler,如圖 4:


圖 4. 擴展點的選取
圖 4. 擴展點的選取

在模板的導航頁面中,可輸入動作的包名,動作的處理 Java 類以及類型的命名空間(如圖 5),並按 Finish 按鈕。


圖 5.模板中的數據項
圖 5.模板中的數據項

生成器會自動生成類的代碼和 WSDL 文件 wsdl/Sample.wsdl。我們需要將自動生成的 WSDL 文件進行重新命名以滿足我們的命名規則,我們將文件名命名爲 CityName.wsdl,與之關聯的擴展點的細節也需相應的改變,如圖 6:


圖 6. PropertyBrokerDefinition 擴展點中 wsdl 文件的指定
圖 6. PropertyBrokerDefinition 擴展點中 wsdl 文件的指定

我們可以用文本編輯器更改 CityName.wsdl 的內容,也可以用 Toolkit 帶的 Property Broker 的可視化編輯器進行編輯修改。修改後的內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="OrderDetail_Service"
  targetNamespace="http://www.ibm.com/wps/c2a"
  xmlns="http://schemas.xmlsoap.org/wsdl/"
  xmlns:portlet="http://www.ibm.com/wps/c2a"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:tns="http://www.ibm.com/wps/c2a"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<types>
  <xsd:schema targetNamespace="http://www.ibm.com/wps/c2a">
    <xsd:simpleType name="ResultType">
      <xsd:restriction base="xsd:string">
      </xsd:restriction>
    </xsd:simpleType>
  </xsd:schema>
</types>

<message name="CityName">
  <part name="resultFromCitySelection" type="tns:ResultType"/>
</message>

<portType name="CityName_Service">
  <operation name="CityName_Operation">
     <output message="tns:CityName"/>
  </operation>
</portType>

<binding name="CityNameBinding" type="tns:CityName_Service">
  <portlet:binding/>
  <operation name="CityName_Operation">
    
    <portlet:action name="CityNameAction"
      type="standard"
      caption="CityName.GetCityName"
      description="Get city name"
      actionNameParameter="ACTION_NAME"/>
    <output>
      <portlet:param name="CityName_Text" 
    partname="resultFromCitySelection" 
    boundTo="request-attribute" 
    caption="City Name"/>
    </output>
  </operation>
</binding>
</definitions>

在這個組件 com.ibm.rcp.samples.cityselection 中, 其輸出參數只有一個: CityName_Text。我們可以用同樣的方法,定義組件 com.ibm.rcp.samples.citytime 中的輸入輸出參數及動作,wsdl 文件的內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="OrderDetail_Service"
  targetNamespace="http://www.ibm.com/wps/c2a"
  xmlns="http://schemas.xmlsoap.org/wsdl/"
  xmlns:portlet="http://www.ibm.com/wps/c2a"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:tns="http://www.ibm.com/wps/c2a"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<types>
  <xsd:schema targetNamespace="http://www.ibm.com/wps/c2a">
    <xsd:simpleType name="ResultType">
      <xsd:restriction base="xsd:string">
      </xsd:restriction>
    </xsd:simpleType>
  </xsd:schema>
</types>

<message name="CityName">
  <part name="resultFromCitySelection" type="tns:ResultType"/>
</message>

<portType name="CityName_Service">
  <operation name="setCityName">
     <input message="tns:CityName"/>
  </operation>
</portType>

<binding name="CityNameBinding" type="tns:CityName_Service">
  <portlet:binding/>
  <operation name="setCityName">
    
    <portlet:action name="setCityName"
      type="standard"
      caption="CityName.GetCityName"
      description="Get city name"
      actionNameParameter="ACTION_NAME"
      activeOnStartup="true"/>
    <input>
      <portlet:param name="setCityName" 
      partname="resultFromCitySelection" 
      caption="CityTime"/>
    </input>
  </operation>
</binding>
</definitions>

在組件 com.ibm.rcp.samples.citytime 中,定義了一個動作 setCityName 以及這個動作的輸入參數 CityName

消息動作的實現

利用 Expeditor 的 Property Broker,我們可以定義不同級別的動作實現,如果組件所運行的設備沒有 SWT 或 AWT 界面,我們可以用 Eclipse 核心命令的消息處理接口(org.eclipes.core.commands.IHandler)來實現動作,其中 IHandler 接口的 execute() 方法必須實現。PropertyChangeEvent 可以作爲消息的觸發器並且可以通過 ExecutionEvent.getTrigger() 獲得。 以下是處理數據改變時事件響應的動作處理代碼:

public Object execute(ExecutionEvent event) throws ExecutionException {
  final Object eventTrigger = event.getTrigger();
  if (eventTrigger instanceof PropertyChangeEvent){
    final PropertyChangeEvent pce = (PropertyChangeEvent)eventTrigger;
    final PropertyValue value = pce.getPropertyValue();
    //TODO implement your handler

  }
  return null;
}

我們的樣例是基於 Eclipse 的 Expeditor 客戶端,有一個視圖用以界面顯示,這樣我們的事件處理代碼可以基於 org.eclipse.jface.action.Action,需要實現的接口函數是 runWithEvent, 代碼如下:

public class setTimeAction extends Action {

  public void runWithEvent(Event event) {
    if (event instanceof PropertyChangeEvent)
    {
      final PropertyChangeEvent pEvent = (PropertyChangeEvent)event;
      //System.out.println("Event received");
      Thread t = new Thread() { 
        public void run() {
          PropertyValue value = pEvent.getPropertyValue();
          //TODO implement your handler
        } 
      };
      Display display = Display.getDefault();
      display.asyncExec(t);
    }
  }
}

數據的發送

數據變化的發佈可以在任何時候,但一般來講,是用戶通過界面的操作來發起,例如,點擊一個按鈕或選擇列表或表中一行元素等。組件 com.ibm.rcp.samples.cityselection 中,是通過點擊 Send 按鈕來觸發的。Property Broker 的 ChangedProperties() 方法告知 Property Broker,開始向其它組件廣播,該組件的參數發生了變化。ChangedProperties 的第二個參數是一個字符串,是與線接配置中的 sourceentityid 一致,我們用當前的 ViewID 來賦值,ViewID 可以用如下的語句獲得:

viewID = this.getViewSite().getId() + ":" + this.getViewSite().getSecondaryId(); 

下面是數據發送所用的完整代碼,其中 m_sendBtn 是 View 的成員變量,代表 Send 那個按鈕。

m_sendBtn.addSelectionListener(new SelectionAdapter() {
  public void widgetSelected(SelectionEvent arg0) {
    PropertyBroker pb = PropertyBrokerFactory.getBroker();

    PropertyValue[] values =  new PropertyValue[1];
    m_BeijingTime.setText( "Beijing Time: "+new Date().toLocaleString() );
    try {
      Property prop = pb.getProperty("http://www.ibm.com/wps/c2a","CityName_Text");
      if(prop!=null) {
        values[0] = PropertyFactory.createPropertyValue(prop, m_City.getText());
        pb.changedProperties(values,viewID);
      }
    } catch (PropertyBrokerException e) {
      e.printStackTrace();
    }
  }
});   

組件的線接(Wire)

沒有線接的組件是一個個孤立的組件,只有通過線接的組件,組件之間才能進行通訊,以及進行互操作。爲了註冊一個線接,需要對擴展點 com.ibm.rcp.propertybroker.PropertyBrokerWire 作擴展,同時定義一個線接,如圖 7。


圖 7. PropertyBrokerWire 擴展點中數據項的指定
圖 7. PropertyBrokerWire 擴展點中數據項的指定

每個線接有如下的屬性:

屬性名稱 屬性說明
Type 類型,當前只支持 PROPERTY_TO_ACTION 線接類型
sourcename 當前線接的發出者名字
targetname 當前線接的接受者的名字
name 線接的名字
title 線接的題目(與線接不影響)
ownerid 擁有者組件的 id,通過 Property broker 的 API,可以進行查詢
odinal 在 broker 註冊表中的排序
sourceentityid 發出者的實體 id
sourceparam 線接信息中將要發送給動作的參數
targetentityid 接受者的實體 id
targetparam 線接信息中動作接受者的動作參數
enable 當前線接是否可用,註冊時自動設爲可用
uid 唯一 id

通常,實體 id 是視圖的 id,即視圖的第一 id 和第二 id 並在中間加冒號。

如下是我們樣例中的線接在 plugin.xml 的描述:

  <extension
         id="com.ibm.rcp.portlet.wire"
         name="Portlet Wire"
         point="com.ibm.rcp.propertybroker.PropertyBrokerWire">
         <wire
              sourceentityid="com.ibm.rcp.samples.cityselection.views.CityNameView:null"
              sourcename="CityName_Text"
              targetentityid="com.ibm.rcp.samples.citytime.views.CityTimeView:null"
              targetname="setCityName"
              type="PROPERTY_TO_ACTION"/>
  </extension> 

當接受者組件收到 property 更改事件時,通過事件獲得對應的線接對象,利用 SWTHelper 的 locateView 方法根據接受者組件的實體 id,查找到對應的 View,並對 View 進行刷新。動作的完整代碼如下:

public class setTimeAction extends Action {

  public void runWithEvent(Event event) {
    if (event instanceof PropertyChangeEvent)
    {
      final PropertyChangeEvent pEvent = (PropertyChangeEvent)event;
      //System.out.println("Event received");
      Thread t = new Thread() { public void run() {
        PropertyValue value = pEvent.getPropertyValue();
        Wire wire = pEvent.getWireDefinition();
  CityTimeView view = (CityTimeView)SWTHelper.locateView(wire.getTargetEntityId());
        //ColorItem color = new ColorItem();
        String result = null;
        result = (String)value.getValue();
        System.out.println("CityName received is "+result);
        view.setCity(result);
      } };
      Display display = Display.getDefault();
      display.asyncExec(t);
    }
  }
}





回頁首


總結

Lotus Expeditor 是一個跨系統的客戶端平臺,在此平臺上,可以將不同的應用或組件集成在一個窗體中,而這些組件之間可以相互通訊相互操作,但又是鬆散耦合的。本文講述了複合應用的概念,並通過一個 Eclipse 實例,展示了在 Lotus Expeditor 平臺上,複合應用的開發過程。開發人員可以根據這一過程,構建出適合自己企業業務需求的複合應用。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章