2007 年 7 月 27 日
面對市場激烈的競爭,企業需要進行快速響應,以滿足市場的需求,複合應用是提高企業業務的靈活性的一個有效的解決方案, 使企業可以充分利用和擴展現在的資源,節約成本, 最大限度地複用組件。 本文主要討論複合應用的概念以及如何開發基於 Lotus Expeditor 的複合應用,通過一個 Eclipse 實例,展示了具有鬆散耦合的複合應用的開發過程。
複合應用是面向服務架構(SOA)中的關鍵要素,它能爲企業提供業務靈活性的同時,能夠更容易創建新服務或者將現有的IT資產轉化爲可重用的服務,這些服務可以快速適應不斷變化的業務需求。複合應用是由多個相互通訊相互操作的組件組成,而這些組件是鬆散耦合的,並且在其它複合應用中重複使用。一個組件中的數據發生了變化,可以通過消息廣播通知給其它組件,與之線接(Wire)的組件收到消息後進行相應的處理以反映到組件中數據的變化。
本文主要討論複合應用的概念以及如何開發基於 Expeditor 的複合應用,通過一個 Eclipse 實例,展示了具有鬆散耦合的複合應用的開發過程。
|
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 查看器;
構成一個複合應用的組件之間需要進行相互通訊,但由於這些組件可以是由不同的開發商提供,這就需要一個標準規範,用於指導開發人員開發出可以用於複合應用的組件。在 Expeditor 中,通過 Property Broker 提供了不同組件之間進行通訊的能力,組件的開發者可以通過 XML 配置文檔發佈組件的數據或屬性(Property)、消息的動作(Action)以及組件間的線接(Wire),Property Broker 能夠動態的檢測到已經變化的組件的數據,並查找註冊過的與該組件相連接或線接的其它組件中的動作,如果找到相應的動作,Property Broker 觸發該動作使之執行,這樣就完成了兩個鬆耦合組件間的通訊及操作。
在這些相互協作的組件中,提供數據的組件我們稱之爲提供者,而接受數據並進行動作的組件我們稱爲接受者,提供者的數據我們稱爲輸出數據,接受者的接受的數據我們稱爲輸入數據,如圖 1。
圖 1. 數據提供者組件的輸出數據和數據接受者組件的輸入數據
僅僅定義組件的輸出數據或輸入數據,兩個組件還不能進行相互通訊,只有通過 Property Broker 線接(Wire)起來的組件,才能進行相互通訊相互操作。線接的一端是數據提供者組件中的輸出數據,另一端是數據接受者的輸入數據,兩者的數據類型必須一致,如圖 2。
圖 2. 兩個組件的線接(Wire)
無論是組件發佈還是註冊,Property Broker 是通過一種 XML 文檔來完成的,這一文檔的格式是借自 Web 服務描述語言 WSDL(Web Services Description Language)格式,通過這個描述語言,可以描述在複合應用中組件的輸入輸出數據(property)、消息動作(action)以及類型等。
|
本節我們給出一個由純粹 Eclipse 組件構成的複合應用,其功能是將當前北京時間換算爲世界其它時間,通過此樣例,可以詳細瞭解基於 Lotus Expeditor 客戶端的複合應用的構建過程。
此樣例由兩個組件組成,com.ibm.rcp.samples.cityselection
是用戶選取世界城市,並顯示當前的北京時間(當前系統時間),另外一個組件 com.ibm.rcp.samples.citytime
是顯示選中城市的當前當地時間。如圖 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_ACTION
,PORTLET
和 AWT_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.propertybroker
和 com.ibm.rcp.propertybroker.swt
, 然後創建一個 com.ibm.rcp.propertybroker.PropertyBrokerDefinitions
的擴展,這一擴展可自動註冊動作以及對應的輸入輸出數據。Lotus Expeditor Toolkit 提供了一個工具爲 Property Broker 定義模板,利用這一導航式工具,可定義出擴展、WSDL 文件及動作的 Java 類文件。在 Property Broker 定義的模板中,可選取模板 SWT Handler,如圖 4:
圖 4. 擴展點的選取
在模板的導航頁面中,可輸入動作的包名,動作的處理 Java 類以及類型的命名空間(如圖 5),並按 Finish 按鈕。
圖 5.模板中的數據項
生成器會自動生成類的代碼和 WSDL 文件 wsdl/Sample.wsdl。我們需要將自動生成的 WSDL 文件進行重新命名以滿足我們的命名規則,我們將文件名命名爲 CityName.wsdl,與之關聯的擴展點的細節也需相應的改變,如圖 6:
圖 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(); } } }); |
沒有線接的組件是一個個孤立的組件,只有通過線接的組件,組件之間才能進行通訊,以及進行互操作。爲了註冊一個線接,需要對擴展點 com.ibm.rcp.propertybroker.PropertyBrokerWire
作擴展,同時定義一個線接,如圖 7。
圖 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 平臺上,複合應用的開發過程。開發人員可以根據這一過程,構建出適合自己企業業務需求的複合應用。