@轉自developerworks
本文介紹了最新版WSAD 5.1.2上基于 JSF技術的Faces Portlet 框架的特色,從 MVC模式角度與基本的 portlet 進行了比較,并進一步深入分析了Faces portlet 開發過程中的關鍵類的具體含義與功能。
簡介
最
新發布的 IBM WebSphere Studio Application Developer 和 IBM WebSphere Studio
Site Developer 5.1.2 版支持新的行業標準,可以簡化針對豐富 Web 用戶界面、商務邏輯(business
logic)和交互式門戶的開發。全新的快速應用程序開發(RAD)和代碼生成工具是WebSphere Studio
的一大亮點,可以幫助實現項目流程自動化,簡化Java開發,從而加快整個團隊的開發速度。
使
用新的WebSphere
Studio產品來構建豐富的用戶界面、數據連接以及面向Web應用程序的商務邏輯,Java開發人員可以立即獲得工作效率的提升。WebSphere
Studio 支持最近獲得Java Community ProcessSM批準的 JavaServer? Faces (JSF)標準和Java
Community ProcessSM推薦的Service Data Objects(SDO)標準。WebSphere
Studio使用這些標準來最大限度地減少構建數據驅動的Web應用程序和豐富的Web用戶界面所需要的Java編碼和手動操作。JSF和SDO的可視化
工具使開發人員可以方便地將JSF用戶界面(UI)組件拖放到頁面,點擊即可連接到關系型數據庫的數據源。
因此,借助WSAD V5.1.2開發工具的幫助,我們可以使用JSF相關的技術來實現終端用戶自己開發的小型系統,更大規模的門戶應用,或者是規模龐大的企業系統。
此
外,該版本的WebSphere Studio Application Developer 還包括 IBM WebSphere Portal
測試環境,新的可視化 portlet 開發工具,并支持新行業標準 portlet Application Programming
Interface (API),可確保 portlet 互操作性和便攜性。構建 portlet 的可視化工具可簡化門戶開發,而與 JSF
的集成使其可以輕易地整合 portlet 中的豐富的用戶界面(UI)組件和 Web
窗體。與過去相比,這大大簡化了整個門戶開發過程,速度也大幅提高,但對相應開發人員技能卻沒有很高的要求。 這就是最新的Faces
Portlet開發框架。
本文是有關Faces Portlet開發的系列文章的第一篇,在這些文章中作者會和開發人員談論有關Faces Portlet開發方面很多有趣的話題。
目標讀者:希望讀者閱讀本文之前,能夠對MVC,portlet和JSF有一定的了解,相關的資料可以在參考資料中找到。
出發之前,先來準備我們的開發測試環境吧。
Faces Portlet開發測試環境的準備:
我們使用的開發工具是WSAD V5.1.2,同時還需要安裝IBM提供的Portal Toolkit V5.0.2.2。
-
WSAD V5.1.2
您可以從下面這個鏈接下載到WSAD V5.1.2的試用版,請
下載安裝。
注意:這里一般不能從WSAD的老版本通過更新管理器來升級您的WSAD版本,最方便可靠的辦法是重新安裝這個新的版本。
-
運行環境:
WebSphere Portal V5.0.2或者WebSphere Portal Express V5.0.2 都可以用來發布Faces Portlet應用。
好了,做完上面這一切,你就可以開始著手開發Faces Portlet應用了。
(圖1)
如上圖(圖1),WSAD V5.1.2提供了四種不同的創建portlet應用的向導。開發人員以前最常用的是Basic portlet這個向導,這里我們就先來比較一下Basic portlet和Faces portlet有哪些區別和類似之處。
傳統上Basic portlet的開發過程
http://www.ibm.com/developerworks/cn/websphere/library/techarticles/0403_lynn/0403_lynn.html從上面這篇文章,你可以了解到以前開發一個最簡單的portlet的一般步驟,最基本的步驟如下:
1. 創建目錄結構:使用Basic portlet的創建向導,WSAD會幫你自動創建目錄結構。
2. 準備JSP文件: 準備好需要顯示的所有JSP頁面。
3. 創建Portlet: 創建Portlet類,它通常是繼承于PortletAdapter的,需要實現的最重要的方法是actionPerformed()和doView()。
4. 準備配置文件: 打包發布之前需要維護web.xml以及portlet.xml,通常WSAD會幫你完成這一切。
5. 打包,發布這個應用:將你的應用導出成WAR包,然后在WebSphere Portal Server上安裝,并且把它裝載到某個頁面中,你就可以看到它的外觀了。
Faces Portlet的主要優點
Faces
portlet脫胎于Basic
portlet,并在其基礎上將JSF技術整合進來,充分吸收了JSF的很多優點,包括豐富的用戶界面控件,便捷的事件處理和頁面導航等特性,進一步降低
了開發portlet應用的門檻。如果能夠充分利用WSAD
V5.1.2提供的工具支持的話,開發人員幾乎感覺不到是在開發portlet應用,而只是在開發基于JSF技術的動態Web應用。
正是由于項目中有了下面這些Jar包的支持,Faces portlet才能正常工作。
jsf-api.jar,jsf-ibm.jar,jsf-impl.jar,jsf-portlet.jar
下面,我們再來從實現MVC模式的角度來比較Basic portlet和Faces portlet的異同:
模型(Model)-視圖(View)-控制(Controller)模式
(圖2)
如圖2描述,MVC模式是由下面三個重要的部分組成的:
Model層實現系統的業務邏輯,通常可以用Java Bean或是EJB來實現。
View層用于與用戶的交互,通常用JSP來實現。
Controller層是Model和View之間溝通的橋梁,它用來分派用戶的請求并且選擇恰當的視圖用于顯示,同時,它還將用戶的輸入映射為模型層相應可執行的操作。
Basic portlet和MVC模式的對應關系
(圖3)
如
圖3,在Basic portlet框架中,后臺的business
bean或是EJB則封裝了應用的業務邏輯,對應于Model層;JSP頁面對應于
View層,它們是用戶所看到的內容;而控制器則是一個繼承于PortletAdapter類的Portlet類,其通過actionPerformed
()方法和doView()方法來完成接收用戶請求,分派調用處理函數,選擇適當的視圖跳轉等控制工作。
下面兩個代碼片斷給出了Portlet類中actionPerformed() 和 doView()的樣例。
代碼片斷1:actionPerformed()通常負責接收用戶事件,再分派至相應的處理函數。
public void actionPerformed(ActionEvent event) throws PortletException {
// ActionEvent handler
String actionString = event.getActionString();
// Add action string handler here
PortletRequest request = event.getRequest();
PortletSession session = request.getPortletSession();
actionBean.setSession(session);
actionBean.setRequest(request);
try {
Class actionClass = actionBean.getClass();
Method method = actionClass.getMethod("on_" + actionString, null);
method.invoke(actionBean, null);
} catch (Exception e) {
}
}
|
代碼片斷2:doView()則通常負責根據后臺的業務邏輯選擇指定相應的頁面視圖。
public void doView(PortletRequest request, PortletResponse response)
throws PortletException, IOException {
PortletSession session = request.getPortletSession();
String JSPFile = (String) session.getAttribute(JSPPAGENAME);
if (JSPFile == null) {
// First time enter into the portle
}
// Set actionURI
setActionURIs(request, response);
// Invoke the JSP to render
getPortletConfig().getContext().include(JSPFile, request, response);
}
|
Faces portlet中和mvc模式的對應關系
(圖4)
如圖4,后臺同樣是通過business bean或是EJB,封裝了應用的業務邏輯,對應于Model層;
而View
層則是采用了Faces JSP(這是一種特別的JSP,它的界面是由JSF用戶界面控件組成的)。
相對而言,這里的控制層(Controller)較為復雜,每個Faces JSP頁面都對應著一個Action
Bean,負責處理該頁面上所有的用戶輸入檢驗,事件處理,以及相關頁面之間的跳轉,這些都是Basic
portlet中portlet類中的actionPerformed()和doView()所要做的。
看上去好像以前那個控制器已經被拋棄了,但事實卻不是這樣,正如圖4中Controller右側的虛線框表示的那樣,它被巧妙的隱藏了起來,開發人員不會
感覺到它的存在,但它卻沒有離開,依舊在后面控制著一切。
比較了Basic portlet和Faces portlet在MVC模式的實現上的異同之后,該來深入了解一下Faces portlet了。
深入了解faces portlet項目
1. 目錄結構
通過Faces portlet wizard新建一個Portlet項目,分析一下目錄結構
(圖5)
上面這張圖是一個初始的Portlet項目的目錄圖。
先來看WebContent
- \WebContent
這里會存放所有的JSP頁面,但是當頁面數量很多的時候這并不是一個好辦法。也許可以將頁面分類分別存放在一些子目錄里。
- \WebContent\META-INF
這里通常會有一個METAFEST.MF文件,存放著一些和項目相關的元數據。我們并不需要關心它里面有些什么。
- \WebContent\theme
這里則是用來放置Portlet應用相關的頁面樣式等,諸如css文件,這通常是美工會關心的地方。
- \WebContent\WEB-INF
這里是整個項目的核心重地,存放了項目所有重要的配置文件。
- \WebContent\WEB-INF\lib
這里存放著和Faces Portlet開發框架密切相關的一些Jar包。
- \WebContent\WEB-INF\classes
這里則存放了編譯好的一些Java類文件。
接下來,再來看Java Resources
- \Java Resources\pagecode
這里存放著和WebContent中每個JSP頁面對應的Java類,用來處理JSP頁面的后臺動作,而所有這些都是繼承PageCodeBase這樣一個基類。
之前我們提到過,每個Faces JSP頁面都會有個Action Bean和其相對應,它們應該在Java Resources中,那JSP文件是如何與這個Action Bean關聯起來的呢?
答案在這里,在每個Faces JSP頁面中都有一段注釋,如下面這個例子:
<%-- jsf:codeBehind language="java" location="/JavaSource/pagecode/Master.java" --%><%-- /jsf:codeBehind --%>
通過這段注釋,這個Faces JSP頁面就和/JavaSource/pagecode/Master.java 這個Action Bean建立了對應關系。
注:編輯這段注釋,你可以改變這種對應關系,但似乎在WSAD的編輯器中是沒有辦法直接修改的,你可以用記事本直接在磁盤上修改它。
2. PageCodeBase.java (附件1中是這個類的完整內容)
這個類是構建一切Action Bean的基礎,它封裝了JSF技術的一些最基本的功能。下面就抽取了其中最重要的幾個函數,分別講解它們的作用。
代碼片斷3:
public PageCodeBase() {
facesContext = FacesContext.getCurrentInstance();
requestScope =
(Map) facesContext
.getApplication()
.createValueBinding("#{requestScope}")
.getValue(facesContext);
sessionScope =
(Map) facesContext
.getApplication()
.createValueBinding("#{sessionScope}")
.getValue(facesContext);
applicationScope =
(Map) facesContext
.getApplication()
.createValueBinding("#{applicationScope}")
.getValue(facesContext);
requestParam =
(Map) facesContext
.getApplication()
.createValueBinding("#{param}")
.getValue(facesContext);
}
|
這
個函數是PageCodeBase這個類的構造器,它負責獲取當前FacesContext的實例之后,將FacesContext之間傳遞數據的4個
Map結構中的數據提取出來,分別是#{requestScope},"#{sessionScope}","#{applicationScope}
","#{param}"它們分別表示在request范圍內,在session范圍內,在application范圍內共享的變量以及頁面之間需要傳遞
的參數。
代碼片斷4:
protected void gotoPage(String pageName) {
if (pageName != null) {
UIViewRoot newView =
facesContext.getApplication().getViewHandler().createView(
facesContext,
pageName);
facesContext.setViewRoot(newView);
}
}
|
這個函數實現了基本的頁面跳轉的功能,通過gotoPage(String pageName)這個函數,頁面可以重定向到新的頁面。
代碼片斷5:
public static UIComponent findComponent(UIComponent base, String id) {
// Is the "base" component itself the match we are looking for?
if (id.equals(base.getId())) {
return base;
}
// Search through our facets and children
UIComponent kid = null;
UIComponent result = null;
Iterator kids = base.getFacetsAndChildren();
while (kids.hasNext() && (result == null)) {
kid = (UIComponent) kids.next();
if (id.equals(kid.getId())) {
result = kid;
break;
}
result = findComponent(kid, id);
if (result != null) {
break;
}
}
return result;
}
|
這個函數一般情況下不會調用,在JSF中,每個頁面中包含的所有的控件根據相互之間的嵌套關系組成一個控件樹,這個函數的作用就是在控件樹上查找并返回指定的控件實例。
代碼片斷6:
protected void putTreeAttribute(String key, Object value) {
getFacesContext().getViewRoot().getAttributes().put(key, value);
}
protected Object getTreeAttribute(String key) {
return getFacesContext().getViewRoot().getAttributes().get(key);
}
|
頁面之間很多數據都是以該頁面所對應的控件樹的某些屬性值的形式來存儲的, 這兩個函數可以讓開發人員在代碼中存取一些需要特殊維護的數據。
代碼片斷7:
protected Object resolveExpression(String expression) {
Object value = null;
if ((expression.indexOf("#{") != -1)
&& (expression.indexOf("#{") < expression.indexOf('}'))) {
value =
getFacesContext().getApplication().createValueBinding(
expression).getValue(
getFacesContext());
} else {
value = expression;
}
return value;
}
|
為什么在Faces JSP頁面中出現的所有變量都是以#{beanname.fieldname}來存在呢,奧秘就在這個函數,Faces Portlet框架內部解析變量時就默認采用了這樣一種格式。
代碼片斷8:
protected void resolveParams(
Map paramMap,
String[] argNames,
String[] argValues,
String cacheMapKey) {
Object rawCache = getTreeAttribute(cacheMapKey);
Map cache = Collections.EMPTY_MAP;
if (rawCache instanceof Map) {
cache = (Map) rawCache;
}
for (int i = 0; i < argNames.length; i++) {
Object result = resolveExpression(argValues[i]);
if (result == null) {
result = cache.get(argNames[i]);
}
paramMap.put(argNames[i], result);
}
putTreeAttribute(cacheMapKey, paramMap);
}
|
這個函數是用來解析頁面之間傳遞的參數的,通常不需要開發人員來調用。
代碼片斷9:
protected static String getRealPath(String relPath) {
String path = relPath;
try {
URL url =
FacesContext
.getCurrentInstance()
.getExternalContext()
.getResource(
relPath);
if (url != null) {
path = url.getPath();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return path;
}
|
這個函數用來返回當前的FacesContext所對應Faces JSP頁面的絕對路徑,在某些場合下非常有用。
代碼片斷10:
protected void logException(Throwable throwable) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
throwable.printStackTrace(printWriter);
log(stringWriter.toString());
}
|
這個函數在調試程序時非常有用,它將發生的異常的StackTrace重定向到日志文件中而不是簡單的打印在控制臺上。
3. faces-config.xml
這個配置文件存儲了幾乎所有和Faces portlet密切相關的配置信息。從
http://java.sun.com/dtd/web-facesconfig_1_0.dtd你可以找到它的DTD的具體內容。
不可能解釋所有的屬性和標簽,這里通過一個具體的例子(附件2中是它的完整內容)來熟悉最常用的一些屬性。
代碼片斷11:
<lifecycle>
<phase-listener>com.ibm.faces.webapp.ValueResourcePhaseListener
</phase-listener>
</lifecycle>
|
這里定義了在JSF的頁面生命周期每個階段狀態的監聽器,默認是不需要修改的,這里使用的是IBM提供的實現:com.ibm.faces.webapp.ValueResourcePhaseListener
代碼片斷12:
<factory>
<faces-context-factory>
com.ibm.faces.context.WPPortletFacesContextFactoryImpl
</faces-context-factory>
</factory>
|
這里則定義了和FacesContext相關的類工廠實現,這里默認也是IBM提供的實現:com.ibm.faces.context.WPPortletFacesContextFactoryImpl
代碼片斷13:
<application>
<locale-config>
<default-locale>zh_CN</default-locale>
<supported-locale>en_US</supported-locale>
<supported-locale>de_DE</supported-locale>
<supported-locale>fr_FR</supported-locale>
</locale-config>
<message-bundle>messagebundle</message-bundle>
</application>
|
這里定義了這個Portlet應用支持的locale的種類,特別指出了默認支持的locale,并且還指定了應用中存儲各種類型的message所需要的message bundle的名稱。
代碼片斷14:
<managed-bean>
<managed-bean-name>pc_PTestView</managed-bean-name>
<managed-bean-class>pagecode.PTestView</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>businessbean</managed-bean-name>
<managed-bean-class>pagecode.businessbean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
|
在Faces portlet應用中,任何使用的Java Bean都會在配置文件中注冊為一個managed bean,包括變量引用時使用的名稱,具體的實現類名,以及它們的使用范圍(request, session, application)
代碼片斷15:
<navigation-rule>
<display-name>navigation rule sample</display-name>
<from-view-id>/master.jsp</from-view-id>
<navigation-case>
<from-action>#{pc_Master.doDetailsBtnAction}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/details.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{pc_Master.doDetailsBtnAction}</from-action>
<from-outcome>failure</from-outcome>
<to-view-id>/homepage.jsp</to-view-id>
</navigation-case>
</navigation-rule>
|
這段也是常見的配置信息,它定義了portlet應用中各個頁面之間的跳轉關系。
from-view-id 定義了這條跳轉規則中的源頁面
<navigation-case></navigation-case> 則通過這個標簽定義了針對一個源頁面的各種跳轉情況。
<from-action> Faces portlet中的跳轉規則是根據某個事件處理函數的返回值來判斷的,這里就定義了具體事件處理函數的名稱。
<from-outcome> 這里則定義了這種跳轉情形所對應的事件處理函數的返回值。<to-view-id> 這里定義了在事件處理函數返回相應的返回值后跳轉的目標頁面。
小結:
Faces Portlet是一個不錯的Portlet應用開發框架,值得去體驗一下。如果你過去有過開發portlet應用的經驗,這些寶貴的經驗會讓你事半功倍。 希望這篇文章能夠讓你對Faces portlet印象深刻。
參考資料
-
http://publib.boulder.ibm.com/infocenter/wsphelp/index.jspInformation Center of WebSphere Studio and WebSphere Application Server
-
http://publib.boulder.ibm.com/pvc/wp/502/ent/en/InfoCenter/index.htmlInformation Center of WebSphere Portal Server v5.0.2
- WSAD V5.1.2的聯機幫助,它會隨WSAD一起安裝。
-
http://www.ibm.com/developerworks/cn/cnwsdd.nsf/wsdd-onlinecourse-bynewest/7DF1D2C22B4ACCD9C8256E7E00230C9D?OpenDocument教程:創建基于 Web 的用戶界面--使用 WebSphere Studio V5.1.1 來開發 JavaServer Faces 應用程序
-
http://www.ibm.com/developerworks/cn/wsdd/library/techarticles/0403_lynn/0403_lynn.shtmlHello World -- WebSphere Portal V5 最簡單的 portlet
-
http://www.jcp.org/en/jsr/detail?id=127JSR127,這是JCP通過的JSF規范
-
http://www.jsfcentral.com/非常不錯的一個關于JSF的開發社區
-
http://www.ibm.com/developerworks/cn/wsdd/zones/studio/theme/studionewtech.shtmlWSStudio新功能入門專題
- 在
www.google.com上搜索關鍵字MVC,你可以很快找到很多關于MVC的材料。