1. 程式人生 > >基於事件偵聽與狀態模式轉換的Portlet開發

基於事件偵聽與狀態模式轉換的Portlet開發

1.1  概念與前提 

要讀懂這節內容,並學會使用狀態模式開發Portlet,你必須具備這裡提到的幾種設計思路,並具備基本的Java開發技能。這裡我們選用的開發工具是 IBM Rational Application Developer以及Portlet Toolkit,你需要熟悉該工具,並懂得如何建立PortletPortlet所需的包、類、頁面,以及執行環境所需的.jar

1.1.1  狀態模式應用於Portlet

StateManagerPortlet是主要的Portlet並且是Portlet無關的。它用作分派器以支援駐留著Portlet程式碼的操作和狀態類

特定於Portlet的控制器程式碼就存在於該類中。擴充套件Action抽象類的類實現了actionPerformed方法該類執行實現特定的操作請求行為所必需的任何控制器功能。

實現狀態介面的類將實現一個performView方法該方法是由 StateManagerPortlet service 方法呼叫的。該方法還包含通常駐留在這些方法中的程式碼。同樣這段程式碼也是特定於它所駐留的狀態類的因此就避免了確定請求的操作帶來的所有額外的控制邏輯混亂。

應用狀態模式會使實現變得簡潔。而且當在Portlet的各模式之間切換時狀態總是被記住的。有了這種模式,你就可以輕鬆地確定想要何時以及在何處從源檢索資料

還可以確定何時應該從快取記憶體檢索資料。由於在門戶頁面重新整理時不呼叫actionPerformed方法所以可以將資料訪問程式碼放在操作狀態中並在那裡快取記憶體它們。狀態類可以使用快取記憶體中的資料以避免重新整理門戶頁面時多次存取資料。

1.1.2  Portlet為什麼使用狀態模式

考慮簡單的基於 MVC Portlet它從遠端機器上將包含一個產品下載配置引數列表的XML檔案拷貝到本地(Portal系統所在的主機),檢索各項引數,並把列表呈現給使用者。在使用者選擇了某一項後將顯示詳細的檢視該檢視顯示的是被選項的詳細資訊。這個詳細檢視可能涉及向資料庫再次發出請求。

有編輯許可權的使用者可以進入到Portlet的編輯模式,檢視並修改XML檔案的內容,儲存後如果發現XML檔案已經做了修改,則傳回遠端機器;否則,仍將檔案包存在Portal本地,以免佔用系統資源。

第一步控制器必須呼叫業務物件來建立合適的Bean,該Bean被傳遞給JSP以用於主檢視中的顯示。第二步控制器在使用者的請求中檢索被選項的指示器然後呼叫業務物件來建立合適的Bean,該Bean被傳遞給不同的 JSP以用於詳細檢視中的顯示。

MVC實現中檢視元件顯然是不同的這兩種檢視需要有兩種不同的JSP可能需要訪問不同的業務模型元件來生成每個請求所需的Bean。控制器函式需要知道:

— 呼叫哪些業務方法

— 生成哪些Bean

— 把這些請求物件上的Bean放在哪裡

— 如何呼叫合適的JSP

這就是程式碼變得複雜的地方。如果使用Servlet程式設計那麼可能選擇把它們實現為不同的Servlet以使每個合適的Servlet 類的服務方法自然地分隔每個請求的控制器函式。如果更喜歡使用分派器方式而不是不同的Servlet實現那麼必須使用框架例如Struts來實現類似的MVC設計。

這是複雜的任務因為不能選擇使用多個Portlet 類來實現一個 Portlet不能直接處理 Portlet類。相同的Portlet的服務方法用來處理返回給Portlet的所有HTTP請求因此需要實現控制程式碼類具有如下功能。

— 確定正在請求哪個操作

— 需要進行哪些處理

— 使Portlet 處於哪狀態

— 顯示什麼內容返回給使用者

1.2  需求分析

1.2.1  Portlet功能需求

我們在Linux主機 test.cn.ibm.com上執行著一個自動下載的程式,要下載的產品列表及各產品的引數儲存在 /home/isc/downLoadConfig/下,檔名為AutodownConfig.xml。我們在Windows 2000 Server 主機Netecauto01上執行著IBM WebSphere Portal 5.1,普通使用者只能檢視產品列表及配置許可權,當該使用者要檢視配置資訊時,Portlet 能從伺服器上將檔案AutodownConfig.xml拷貝到本地伺服器,並讀出XML檔案中的內容,顯示在列表中。具有管理許可權的使用者還有修改各配置資訊的許可權,能對產品配置資訊的各項引數進行修改。修改完成後,要使該配置有效,必須將該XML檔案回傳到pvcent07的存放目錄。

AutoDownLoadConfig Portlet 維護一個自動下載的產品配置列表在編輯模式下使用者可以瀏覽下載產品列表檢視一個選定的下載產品的詳細資訊也可以修改一個產品的配置資訊。在配置模式下使用者可以設定資料訪問資訊。雖然可用的資料來源提供了包括配置模式在內的完整的實現但為了滿足設計目的,我們只需注意檢視和編輯模式的實現即可自動下載Portlet狀態模式示意圖如圖1-1所示。

基於事件偵聽與狀態模式轉換的Portlet開發2148.png

1-1  自動下載Portlet狀態模式示意圖

1.2.2  基於MVC模型的角度架構邏輯

1.以基於MVC架構的思路整理Portlet的邏輯

從可視的角度看,圖1-1所示的場景功能的實現至少需要如下頁面

— 主檢視頁面Main View Page顯示所有正在下載的產品列表和用來選擇一個產品以獲取更多詳細資訊的選項

— 詳細資訊檢視頁面Detail View Page顯示被選擇產品的詳細配置資訊

— 主編輯頁面Main Edit Page顯示使用者有許可權編輯的產品列表和用於修改更多資訊的選項

— 修改條目頁面Modify Entry Page顯示相似的表單其中插入了現有的資料用於修改,包括修改單項引數,以及給某一引數增刪條目列表。 

在這種情況下使用者可以從主編輯頁面中選擇需編輯產品條目。修改後,沒有確認頁面或成功執行的頁面。進行條目修改處理並重新整理主編輯頁面若出錯則顯示適當的訊息但在正常處理時沒有與該操作關聯的檢視。

如何來改進這個實現呢?首先請記住這個應用程式表示一組應用程式操作和狀態;其次操作是實現操作介面的類這個類實際完成某個應用程式任務或操作處理。這就是目前存在於 actionListener 類的 actionPerformed 方法中的應用程式程式碼的一部分只有這部分特定於單個操作事件。

狀態是實現狀態介面的類這個類表示由於應用了操作而產生的 Portlet 的效果。一般來說這個類有可視元件。

2Portlet所包含的內容

根據以上分析,我們將有下面這些操作 

— 所有產品配置資訊的主列表檢視

— 顯示選定產品的配置詳細資訊檢視

— 顯示所有可編輯產品的編輯檢視列表

— 顯示檢視來修改一個產品的配置資訊。

— 為產品新增一個可選的下載項或郵件。

— 為產品刪除一個可選的下載項或郵件。

執行這些操作會產生下列狀態之一

— 主檢視

— 詳細資訊檢視

— 主編輯檢視

— 新增下載產品檢視

— 修改下載產品檢視

從可視角度看下載產品列表 Portlet 的實現包括如下檢視頁面)。

1主檢視Main View顯示下載產品列表並帶有選項來選擇下載產品以檢視更多的資訊。

2詳細資訊檢視Detail View顯示選定的下載產品的詳細資訊。

3主編輯檢視Main Edit View顯示下載產品列表並帶有選項來新增、刪除和修改下載產品資訊。

4新增下載產品檢視Add Contact View顯示錶單檢視來輸入新下載產品的資訊。

5修改下載產品檢視Modify Contact View顯示相似的表單檢視來新增現有的下載產品的元素資料以供修改。

1.3  Portlet詳細設計

1.3.1  程式流程設計

下面以圖示的方式來介紹程式的資料流轉。

 在顯示模式下,Portlet會從遠端pvcent07.cn.ibm.com拷回XML檔案,讀出並顯示所有正在下載的產品列表。單擊列表中的任意項,將顯示該項的詳細檢視。我們先來看產品下載列表,如圖1-2所示。

基於事件偵聽與狀態模式轉換的Portlet開發3443.png

1-2  產品下載列表

單擊任意產品的連結後,將顯示該產品的具體配置引數,如圖1-3所示。

基於事件偵聽與狀態模式轉換的Portlet開發3494.png

1-3  產品的具體配置引數

在編輯模式下,首先顯示的是使用者有許可權編輯的產品配置列表,如圖1-4所示。

基於事件偵聽與狀態模式轉換的Portlet開發3551.png

1-4  使用者有許可權編輯的產品配置列表

單擊任意產品的連結,進入到該產品的編輯介面,如圖1-5所示


基於事件偵聽與狀態模式轉換的Portlet開發3624.png

1-5  產品的編輯介面

使用者可以為該產品新增CD介質,或者新增一個E-mail,如圖1-6所示。


基於事件偵聽與狀態模式轉換的Portlet開發3696.png

1-6  為產品新增要下載的CD介質

當然,使用者也可以刪除要下載的CD介質或者E-mail,如圖1-7所示。

基於事件偵聽與狀態模式轉換的Portlet開發3773.png

1-7  刪除要下載的CD介質

使用者可以修改任意引數,然後單擊Save To XML”按鈕,Portlet將把這些引數儲存到XML檔案,並傳回到pvcent機器,如圖1-8所示。

基於事件偵聽與狀態模式轉換的Portlet開發3886.png

1-8  將修改儲存到Portlet配置檔案

 確認修改儲存成功,如圖1-9 所示。


基於事件偵聽與狀態模式轉換的Portlet開發3950.png

1-9  確認修改儲存成功

1.3.2  Actions States總體設計

1StateManagerPortlet

StateManagerPortlet是主要的PortletPortlet無關的一般包括所有特定於 Portlet 的程式碼。該類被用作分派器以支援駐留著Portlet 程式碼的操作和狀態類。StateManagerPortlet實現了actionPerformeddoViewdoEditdoHelpdoConfigure方法。

actionPerformed方法只是獲取操作類的當前例項然後分派到它的actionPerformed方法。類似地do方法獲取當前的狀態物件然後分派到它的perform方法。所以StateManagerPortlet 不需要知道當前Portlet實現的任何細節也沒有大量用來確定下一步處理的if和檢查。只要熟悉狀態與操作之間的流程處理就能正確地進行而不必編寫太多的、多餘的控制邏輯程式碼。

2ActionClassManager

WebSphere PortalActionClassManager方法把Action類例項新增到已棄用的PortelURI中。這個API是有用的因為可以從PortletEvent物件中檢索Action子類例項並把該例項作為狀態轉換的一部分分派到它的actionPerformed方法中。推薦的API將使用字串而不是Action來新增到PortletURI並且從Portlet Event 中進行檢索。ActionClassManager類提供了從給定的字串到Action 類的例項對映。

— Action

實現這個介面的類將實現actionPerformed方法該方法執行任何操作以實現操作請求所需的函式。但是實現的函式特定於正被呼叫的操作事件。某個操作類的個別的actionPerformed方法僅包含該操作的程式碼該方法還為進一步的處理設定當前狀態。在這個流程中操作被呼叫後它進行特定於函式的工作然後為下一次轉換設定狀態。

— InitialStateManager

該類為支援的每個 Portlet方式提供初始狀態。

— State

一般來說Stateperform方法將呼叫JSP來顯示它的結果。使用者介面可能讓使用者來設定Portlet中的其他操作。JSP使操作類與頁面上的每個操作關聯。在使用者呼叫其中的一個操作時StateManagerPortletactionPerformed方法將呼叫合適的操作類例項接著發生了狀態轉換。狀態類並不負責狀態管理和轉換。

實現這個介面的類需實現perform方法該方法可被StateManagerPortletdo方法呼叫它包含一般駐留在這些方法中的程式碼。同樣這些程式碼特定於它們所在類的狀態從而降低了複雜性。

1.3.3  程式碼類設計、操作類清單

我們所建立的各個類的功能分別簡單介紹如下。

1213.png

1State類功能邏輯介紹

子類1ActionClassManager

ActionClassManager負責返回一個操作子類例項該例項給出一個類識別符號這個識別符號是通過 addAction字串方法新增到 PortletURI中的。我們遵循如下約定使類識別符號為全限定類名這樣我們就能夠很容易地建立一個類的例項。然而我們可以通過單獨處理操作子類例項來避免不必要的物件建立然後需要ActionClassManager為所引用的操作子類返回單一的例項。

子類2ActionPerformed

抽象的操作類定義了兩個抽象的方法因此它的子類必須實現 actionPerformed 方法和 setState 方法。當 StateManagerPortlet 類從它的 actionPerformed 方法分派處理時操作子類的 actionPerformed 方法得到呼叫。

setState方法也可由 StateManagerPortlet呼叫確保操作子類的實現在該使用者請求的操作處理完成之後設定下一個狀態。操作類實現它自己的setState方法子類必須呼叫該方法來真正設定下一個狀態。這確保了操作類和狀態類知道在哪類設定和恢復下一個狀態而不需要子類知道這些機制。這些狀態跨HTTP請求並維持在每Portlet模式下。所以當用戶改變模式時控制返回到最近一次訪問的狀態。

子類3MainViewAction操作類

MainViewAction 類並不需要任何特定的操作處理。事實上我們需要將下載產品列表顯示在主檢視上。我們能夠實現在actionPerformed方法中建立恰當的下載產品列表Bean的程式碼將該程式碼設定在會話或請求物件裡然後將下一個狀態設定到MainViewState裡。 performView implementation繼承MainViewAction操作類,能夠簡單地呼叫JSP並提交來自會話或請求的Bean裡的列表內容。然而我們必須注意門戶頁面更新以防更新時呼叫的是PortletMainViewStateperformView方法,而非MainViewActionactionPerformed方法。所以我們不能只是將Bean放置於請求物件裡而沒有讓 performView 方法重新建立該Bean並將新的Bean放在該請求物件裡。我們可以在會話裡再次使用該Bean。但如果那樣做的話資料將會過時。如果我們在編輯模式下做了變動就需要通知該變化並在 State 方法裡重新整理資料Bean。為了簡化這種處理我們會只為 MainViewState State 方法裡得到資料 Bean

所以 MainViewState 類裡actionPerformed方法並沒有另外處理只是簡單設定了MainViewState而已。

子類4StateManagerPortlet

actionPerformed 完成處理時控制返回到門戶容器以便其他的偵聽器通知處理程式執行。然後門戶容器通過呼叫 StateManagerPortlet service 方法繼續 Portlet 請求處理。

StateManagerPortlet service 方法首先試圖檢索以前執行的操作類設定的狀態類如果沒有發現狀態類引用例如最初的 Portlet 呼叫),那麼就使用一個助手類 InitialStateManager 來為每Portlet 模式確定初始的狀態類。InitialStateManager 有一個一的方法叫做 getInitialState它根據 Portlet 的當前模式返回狀態物件的一個例項。對於下載產品列表 Portlet這個類將為檢視模式返回 MainViewState 的一個例項。

State介面有一個一的方法 performView所有的狀態類都必須實現它。StateManagerPortlet service 方法呼叫這個方法。

子類5MainViewState

MainViewState performView 方法負責在 Contacts Bean 的列表表單中獲取下載產品列表下載產品列表儲存在資料庫裡然後傳遞到 JSP 以便在主檢視中呈現出來。

子類6MainViewState

這個子類允許使用者單擊一個下載產品條目然後顯示該下載產品的詳細資訊檢視。

用於這個標記的 href 使用來自Portlet標記庫中的createURI標記。該標記在URIAction中獲得引數URIAction被我們設定為操作類中用於使用者單擊操作的事件處理方法的名稱。

子類7actionPerformed

actionPerformed類用來檢視JSP上顯示的主要清單。使用者可以從主檢視頁面選擇特定的下載產品條目來獲得詳細資訊。我們還添加了一個錨標記將下載產品條目的名稱作為可點選的連結顯示出來擊時StateManagerPortletactionPerformed 方法被DetailViewAction 類呼叫。

actionPerformed方法得到所選擇的下載產品條目的物件id並呼叫永續性類的代理為該id獲取例項化的下載產品物件。這個下載產品物件放在State類的會話中用來呈現詳細資訊檢視。如果該頁面由於門戶頁面重新整理而得以重新整理那麼該Bean就將在會話中可用既然該物件只會通過使用者使用 Portlet 來獲得更新我們就不必擔心資料過時了。

門戶頁面重新整理的另一個關鍵之處是表單資料沒有被重新初始化。如果我們試圖獲得所選下載產品的物件id作為狀態類的請求引數它在門戶頁面重新整理時將不可用。因此為了使 Portlet 能正常執行必須在剛開始時就檢索資料元素然後將其儲存在某個地方這樣在隨後的頁面重新整理時它才可以被引用。由於 actionPerformed 方法曾被呼叫所以這就是存放這段程式碼的好地方。

actionPerformed方法中保留後端資料訪問可以確保我們在頁面重新整理時對於同一資料不需要多次訪問資料庫。當然返回到資料來源進行資料重新整理的時間和頻率取決於Portlet的需求以及資料本身的因素。在這種情況下資料不是動態的 Portlet 頁面被重新整理時應該對其進行快取記憶體。

DetailViewState 被設定為這個Portlet的下一個狀態對於Portlet其中的處理將繼續。

子類8DetailViewState

DetailViewState 簡單地呼叫JSP來呈現詳細資訊檢視JSP 從會話中獲取下載產品 Bean。在UI互動介面中當用戶單擊OK按鈕時處理返回到主檢視繼續進行。MainViewActionactionPerformed 方法通過刪除我們在 DetailViewAction 類中設定的下載產品物件 id 來簡單地刪除會話資料。

子類9ViewProductAction

ViewProductAction類用來顯示下載產品詳細資訊檢視JSP頁面。流邏輯以一種組織良好的方式進行,你不需要在Portlet使用冗的控制程式碼。Portlet 的編輯和配置模式也以同樣的方法實現。

2完成Portlet實現

該應用程式的其餘部分仍遵循以上所述的開發模式。編輯模式的處理控制邏輯流和檢視模式的完全一樣。我們實現一配置模式它允許使用者指定資料來源來儲存下載產品資料。它將以同樣的方式實現。配置模式只有一個檢視來讓使用者訪問資料來源另外有兩個類來檢驗資料來源和儲存資料來源為配置資料。

下面是實現狀態模式的基本步驟。

  所執行的特定模式的初始狀態類由 InitialStateManager 類提供。

狀態類的 performView方法其呼叫是由 StateManagerPortlet 分派的進行任何必要的應用程式邏輯處理然後呼叫它的 JSP

通過PortletURI上的一個引數將每個使用者的操作與在其中進行事件處理的操作子類的名稱相關聯。每個使用者操作一般都指定一個不同的操作子類該子類實現一個單一的、特定的功能。

使用者單擊一個連結時操作類的 actionPerformed 方法被呼叫由於通過 StateManagerPortlet 分派而被再次呼叫。執行操作邏輯並設定適當的狀態類以使處理繼續進行。

當事件處理完成後StateManagerPortletservice方法被呼叫並再次分派給在Action事件處理階段設定的狀態類該狀態類執行應用程式邏輯並呼叫它的 JSP 來呈現結果。

當用戶瀏覽整個 Portlet 操作以這種方式繼續。

3永續性代理

訪問資料庫中的永續性資料需要額外的 Portlet 元件。AbstractBroker 類提供一般的JDBC資料庫訪問功能。它能夠用來獲取資料庫的DataSourceConnection它在快取記憶體中快取DataSource從而避免重複的、高代價的JNDI查詢它也提供通用程式碼來執行 PreparedStatement並關閉ConnectionStatementResultSet

ContactListBroker 類繼承了 AbstractBroker它實現特定於 Portlet 需要的資料訪問方法 getContact 方法和 getContactList 方法用於儲存刪除一個列表中的條目。同時它還有檢驗表 Schema 的功能這樣我們就可以檢驗使用者在配置模式下指定的資料來源。

4異常處理

我們已經為Portlet 實現了大量的異常類基本類AIMExceptionAIMWrapperExceptionAIMException 的子類。可以用 AIMWrapperException 封裝其他丟擲的異常以便在 StateManagerPortletservice 方法中高效地修改顯示行為和管理異常處理。

AIMMessageException 是一個特殊的異常它允許在終止處理時向 Portlet 生成一條報告性訊息或錯誤訊息。例如可以丟擲一個 AIMMessage 異常用一條訊息指出使用者必須先登入。

actionPerformed方法或setState方法中丟擲的異常被捕獲並且被延遲到 service 方法。管理延遲的方法是捕獲這些方法中丟擲的所有異常並將異常通常封裝在一個AIMWrapperException放在請求物件上。當呼叫StateManagerPortletservice方法時它首先查詢有沒有延遲異常如果找到了就從此處重新丟擲並處理這些異常。

因此所有的Portlet應用程式異常都是在 StateManagerPortletservice方法中處理的。如果發現了異常,則在以下兩種情況中呼叫異常方法異常訊息將在Portlet中被顯示異常訊息以及相關聯的堆疊跟蹤資訊將被顯示。將堆疊跟蹤資訊放在 Portlet 外有助於在開發時進行除錯。由於不想向實際應用的使用者顯示這種級別的詳細資訊所以該行為是可配置的。這是在部署描述符裡定義的 debugTrace引數。

5建立開發環境

如果使用WebSphere Studio來建立這個Portlet請確保下列JAR檔案構建路徑是可用的以便Portlet程式碼可以順利編譯。如果Studio中使用Portal Toolkit並建立Portlet應用專案那麼類路徑會自動建立。不管哪種情況,你都可以建立一個Web專案或Portlet應用專案然後匯入下載中的WAR檔案從而將這個Portlet應用程式載入Studio

SERVERJDK_50_PLUGINDIR/jre/lib/rt.jar

WAS_50_PLUGINDIR/lib/dynacache.jar

WAS_50_PLUGINDIR/lib/j2ee.jar

WAS_50_PLUGINDIR/lib/servletevent.jar

WAS_50_PLUGINDIR/lib/ivjejb35.jar

WAS_50_PLUGINDIR/lib/runtime.jar

WAS_50_PLUGINDIR/lib/ras.jar

WAS_50_PLUGINDIR/lib/naming.jar

WAS_50_PLUGINDIR/lib/utils.jar

WPS_V5_PLUGINDIR/portlet-api.jar

WPS_V5_PLUGINDIR/wpsportlets.jar

WPS_V5_PLUGINDIR/wps.jar

如果沒有使用 Portal Toolkit或者Studio,那麼可以在 <WAS_ROOT>/lib <WPS_ROOT>/shared/app 中找到這些檔案。

Portlet部署到WebSphere Portal 環境中時所有必需的JAR檔案都要同時打包進去可以不修改 Portal 中安裝 Portlet WAR 檔案。

1.3.5  RAD實現Portlet

針對入門者,我們以圖示的方式,向大家介紹使用IBM提供的開發工具RADRational Application Developer)來建立、開發、除錯、打包Portlet

原則上我是按照安裝從始至終的次序來截圖的,但為了使層次更清晰,我們還是分為以下7個步驟來分別介紹。

1.安裝RAD,建立portlet

Windows 2003 Server系統的“控制面板”高階”選項中設定”按鈕,在出現的對話方塊中選擇資料執行儲存面板,新增...按鈕,把安裝後的rationalsdp.exeenroll.exe 新增到列表中。

開啟RAD,單擊“新建”按鈕,選擇“專案”→“Portlet專案”,然後依次輸入各項引數,包括Portlet的名稱、包名、是否