Portlet 應用開發, Part 6 - Portlet 的 Preference對象
By Jia.li(Terry.li) SpiritSeekerS@sqatester.com
本部分將闡述了Portlet的Preference對象, Preference 對象是Portlet所特有的對象,用來實現用户的個性化設置,可以替代部分數據庫的功能.
• 為什麼使用Preference 對象?
Preference 主要用來幫助用户對Portlet進行符合用户需要的顯示定製或者行為定製.
舉一個簡單的例子: 用户有一個用來顯示商品列表的Portlet, 可能有些用户需要在Portlet窗口中每頁顯示10個商品, 但是也有用户需要在Portlet的窗口中顯示20個或者是30個商品. 這樣的話, 對於同一個Portlet應用, 用户可以定製自己滿意的用户界面.
以上的Preference 使用有一個前提, Preference對象只用來存取簡單的配置信息,並不能替代數據庫的應用.
• 如何使用Preference 對象?
Preference對象對於Portlet的配置信息存取使用KEY=VALUE , 或者是 KEY=VALUES 形式. 如果你的Portlet有需要使用用户定製的元素,可以將其加入到Preference對象中. 如下:
PortletPreference p= req.getPortletPreferences();
p.setValue(“PageSize”,”10”);
p.store();
• Preference的屬性的作用範圍
因為Preference的屬性是用來存取用户的個性化信息的, 因此兩個用户之間不可以share屬性.
注: Pluto因為是單用户的Portal/PortletContainer的實現,因此如果兩個用户使用同一個preference屬性名稱,互相會有衝突.
• 如何配置Preference 屬性?
在Portlet.xml中設置Preference的初始或者是默認屬性. 如下:
<portlet-preferences>
<preference>
<name>PageSize</name>
<value>20</value>
<read-only>true</read-only>
</preference>
</portlet-preferences>
在以上配置中配置了一個preference 屬性PageSize, 注意read-only標籤中設置了true, 那麼這樣一來這個preference 屬性便不可以通過編程更改.
• 為什麼使用PreferencesValidator對象?
PreferencesValidator對象允許Portlet的preference在被儲存之前進行驗證.用以保證portlet的Preference存取的正確性.
• 如何配置PreferencesValidator對象 ?
Servlet開發中有一種叫filter Servlet, 它的配置和PreferencesValidator的配置方式非常相似, 只不過一個在web.xml中配置, 而另一個在portlet.xml中配置. 如下XML:
<portlet-preferences>
<preferences-validator>
com.sss.PortletValidator
</preferences-validator>
</portlet-preferences>
• Case Study
以下我們將使用Preference和PreferencesValidator對象來開發一個簡單的Portlet實現一個簡單的用户登陸auditing. 以下是代碼片段:
1. Portlet (PortletPreferenceExample.java)
… …
PortletPreferences prefs = request.getPreferences();
prefs.setValue("audit_enabled",audit_enabled);
prefs.store();
… …
以上代碼片段將一個Portlet的配置參數audit_enabled賦予相應的值.
… …
PortletPreferences prefs = request.getPreferences();
prefs.reset("audit_enabled");
prefs.store();
… …
以上代碼片段將一個Portlet的配置參數audit_enabled重新reset到初始時Portlet.xml配置文件中audit_enabled所對應的值.
2. JSP (edit_preference.jsp)
<%
PortletPreferences prefs = renderRequest.getPreferences();
String flag=prefs.getValue("audit_enabled","").toString();
%>
Edit 模式下Fragment 用來取得 Preference 的KEY值的代碼 .
3. JSP (view_portletpreference.jsp)
<%
PortletPreferences prefs = renderRequest.getPreferences();
String flag=prefs.getValue("audit_enabled","").toString();
%>
View 模式下Fragment 用來取得 Preference 的KEY值的代碼.
4. PreferencesValidator (PortletPreferencesValidator.java)
public class PortletPreferencesValidator implements
PreferencesValidator{
… …
… …
public void validate(PortletPreferences preferences)
throws ValidatorException
{
… …
… …
}
}
PortletPreferencesValidator執行了PreferencesValidator接口, 其具體的功能實現由validate方法執行. 我們這裏將應用邏輯在validate方法中執行. 其功能主要是驗證KEY所對應的值是否符合要求.(本例中Validator將驗證audit_enabled所對應的值是否為”0”或者”1”, 否則將拋出錯誤).
• Preference Portlet的運行結果
將Tomcat啓動, 在IE 或者 NetScape 中敲入以下地址:
http://localhost:8080/pluto/portal
單擊Preference Example Page後可以看到如下頁面(圖6-1)
圖6-1
輸入”SpiritSeekerS” , 單擊 Enter Button, 進入如下頁面 (圖6-2)
圖6-2
進入edit mode , 將enable audit 選項選中 , 如下(圖6-3):
圖6-3
單擊 SAVE button , 將回到view mode , 再次登陸後, 如下圖(圖6-4)
圖6-4
• 源代碼及Portlet相關配置文件
1. Portlet (PortletPreferenceExample.java)
package portlets.portletpreference;
/**
* @author terry
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
import javax.portlet.*;
import java.io.IOException;
public class PortletPreferenceExample extends GenericPortlet{
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html");
String jspName = getPortletConfig().getInitParameter("view");
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(jspName);
rd.include(request, response);
}
public void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html");
String jspName = getPortletConfig().getInitParameter("edit");
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(jspName);
rd.include(request, response);
}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException
{
String action = request.getParameter("action");
String audit_enabled = request.getParameter("audit_enabled");
if(action.equals("edit")&&audit_enabled!=null){
PortletPreferences prefs = request.getPreferences();
prefs.setValue("audit_enabled",audit_enabled);
prefs.store();
}else if(action.equals("edit")){
PortletPreferences prefs = request.getPreferences();
prefs.reset("audit_enabled");
prefs.store();
}else if(action.equals("view")){
String usr= request.getParameter("usr");
response.setRenderParameter("usr",usr);
}
else{
throw new PortletException("Unexpected Error!");
}
}
}
2. JSP (view_portletpreference.jsp)
<%@ page session="false" %>
<%@ page import="javax.portlet.*"%>
<%@ page import="java.util.*"%>
<%@ taglib uri='/WEB-INF/tld/portlet.tld' prefix='portlet'%>
<portlet:defineObjects/>
<BR>
<%
PortletPreferences prefs = renderRequest.getPreferences();
String flag=prefs.getValue("audit_enabled","").toString();
%>
<portlet:actionURL portletMode="view" var="url">
<portlet:param name="action" value="view"/>
</portlet:actionURL>
<form method=post action=<%=url%>>
<table border=0>
<tr><td><b>Name:</b></td><td><input type=text name=usr></tr>
<tr><td colspan=2 align=right><input type=submit value="Enter"></td></tr>
</table>
</form>
<%
String usr=renderRequest.getParameter("usr");
if(usr!=null){
%>Welcome, <%=usr%> !! <BR><%
%>
<%
if(flag.equals("1")){
%>
===============================================<BR>
<table border=0>
<tr>
<td><b>INFO:</b> <%=usr.toUpperCase()%> login at </td>
<td><%=new Date().toString()%></td>
</tr>
</table>
===============================================<BR>
<%
}
}
%>
3. JSP (edit_portletpreference.jsp)
<%@ page session="false" %>
<%@ page import="javax.portlet.*"%>
<%@ taglib uri='/WEB-INF/tld/portlet.tld' prefix='portlet'%>
<portlet:defineObjects/>
<BR>
<%
PortletPreferences prefs = renderRequest.getPreferences();
String flag=prefs.getValue("audit_enabled","").toString();
%>
<portlet:actionURL portletMode="view" var="url">
<portlet:param name="action" value="edit"/>
</portlet:actionURL>
<form method=post action=<%=url%>>
<table border=0>
<tr>
<td><b>Auditing Enabled:</b></td>
<td><input type=checkbox name="audit_enabled" value="1" <%=flag.equals("1")?"checked":""%>></td>
</tr>
<tr>
<td colspan=2 align=right><input type=submit value="SAVE"></td>
</tr>
</table>
</form>
4. Preference Validator (PortletPreferencesValidator.java)
package portlets.portletpreference;
/**
* @author terry
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
import javax.portlet.*;
import java.util.*;
public class PortletPreferencesValidator implements PreferencesValidator{
public static final String AUDITING_ENABLED="1";
public static final String AUDITING_DISABLED="0";
public void validate(PortletPreferences preferences)
throws ValidatorException
{
Enumeration prefnms = preferences.getNames();
Collection errKeys = new ArrayList();
while (prefnms.hasMoreElements()){
String prefnm = prefnms.nextElement().toString();
String value = preferences.getValue(prefnm, AUDITING_DISABLED);
System.out.println(value);
if((value.equals(AUDITING_ENABLED))||(value.equals(AUDITING_DISABLED))){
System.out.println("#### INFO: VALID KEY.");
}else{
System.out.println("#### ERROR: INVALID KEY.");
throw new ValidatorException("INVALID KEY",new Throwable("Incorrect Value!"),errKeys);
}
}
}
}
5. Portlet.xml
… …
<!-- Preference Example -->
<portlet>
<description>PortletPreference Example</description>
<portlet-name>PortletPreferenceExample</portlet-name>
<display-name>PortletPreferenceExample</display-name>
<portlet-class>portlets.portletpreference.PortletPreferenceExample</portlet-class>
<init-param>
<name>view</name>
<value>/fragments/portletpreference/view_portletpreference.jsp</value>
</init-param>
<init-param>
<name>edit</name>
<value>/fragments/portletpreference/edit_portletpreference.jsp</value>
</init-param>
<expiration-cache>-1</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
<portlet-mode>EDIT</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<portlet-info>
<title>PortletPreference Example</title>
<short-title>PortletPreference</short-title>
<keywords>PortletPreference</keywords>
</portlet-info>
<portlet-preferences>
<preference>
<name>audit_enabled</name>
<value>0</value>
</preference>
<preferences-validator>portlets.portletpreference.PortletPreferencesValidator</preferences-validator>
</portlet-preferences>
</portlet>
… …
6. pageregistry.xml
… …
<!-- Preference Example Page -->
<fragment name="preferencepage" type="page">
<navigation>
<title>Preference Example Page</title>
<description>Preference Example Page</description>
</navigation>
<fragment name="row1" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="10.70"/>
</fragment>
</fragment>
</fragment>
</fragment>
… …
7. portletregistry.xml
… …
<portlet id="70">
<definition-id>portlets.PortletPreferenceExample</definition-id>
</portlet>
… …
資源:
• Pluto
http://jakarta.apache.org/pluto
• Pluto Mail List
http://news.gmane.org/gmane.comp.jakarta.pluto.user
• WSRP Spec1.0
http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsrp
• Apache的WSRP實現
http://ws.apache.org/wsrp4j/
• Apache’s Portal, JetSpeed:
http://jakarta.apache.org/jetspeed/site/index.html
• JSR 168:
http://www.jcp.org/en/jsr/detail?id=168
• "Portlet 規範介紹" By Stefan Hepper 和 Stephan Hesmer
Part 1: Get your feet wet with the specification's underlying terms and concepts (August 2003)
Part 2: The Portlet API's reference implementation reveals its secrets (September 2003)
Java Portlet 應用開發, Part 5 - Portlet 的 Session對象
By Jia.li(Terry.li) SpiritSeekerS@sqatester.com
本系列前一部分闡述了Portlet的Request和Response對象,本部分繼續講解Portlet中Session對象.及其與Servlet中Session對象的不同點.
• Portlet Session中存取對象的作用範圍 (Scope)
Servlet中的Session屬性和Portlet中的Session屬性有一個非常大的不同點, 由於Portlet處於Portal中的緣故, Portlet的Session屬性分為兩種, 分別作用於不同的範圍中:
1. Application Scope
2. Portlet Scope
這兩者的區別在於:
1) Application Scope類型的PortletSession中保存的對象對於同一個Portlet Application中的所有其它Portlet來説是可以被訪問到的.
2) Portlet Scope類型的PortletSession中保存的對象對於同一個Portlet Application中的所有其它Portlet來説是不可見的.
但對於Portlet Application來説,其實它也是個Web Application , 因此不論是Application Scope也好,或者是Portlet Scope也好,同樣都屬於HttpSession,因此都可以通過HttpSession 來訪問.
實際上Application Scope的Session就是HttpSession , 而Portlet Scope的Session 由於不可被其他Portlet訪問, 它的session屬性名稱有它獨特的存取方式, 如下:
javax.portlet.p.<ID>?<ATTRIBUTE_NAME>
其中ID是Portal/Portlet-Container生成用來唯一確定一個Portlet.
Portlet Scope 的Session 使用以上的Attribute Name 用以存取Session上綁定的屬性. 我們所設制的Portlet Scope 的Session屬性由Portlet容器自動轉化成以上格式. 用以保證不被其它的Portlet訪問到.
注: 以javax.portlet開頭的Session屬性名稱是保留的,不可以用於Portlet的Session 屬性名稱.
• Case study: 不同Scope Session的比較
以下我們將編碼實現兩個Portlet, 其中一個用來創建session屬性的Portlet 和另一個用來驗證session屬性作用範圍的Portlet. 用以驗證Session屬性的正確性.
1. Portlet (PortletSessionExample1_GenerateSesssion.java)
… …
public void processAction(ActionRequest request,ActionResponse response)
throws PortletException,IOException{
String action=request.getParameter("ACTION");
if(action.equals("ApplicationScope")){
PortletSession ps=request.getPortletSession();
ps.setAttribute("PortletSession.AS",action,PortletSession.APPLICATION_SCOPE);
}else{
PortletSession ps=request.getPortletSession();
ps.setAttribute("PortletSession.PS",action,PortletSession.PORTLET_SCOPE);
}
}
… …
以上代碼片段根據不同參數分別產生不同Scope的PortletSession 的屬性 , 作用範圍分別為Application 和 Portlet.
2. Portlet (PortletSessionExample1_DisplaySession.java)
這個Portlet調用一個JSP (jspShowSession.jsp) 來獲得Portlet session和HttpSession, 並且示出來.
3. JSP(view_portletsession1_generatesession.jsp)
這個JSP將創建兩個ActionURL , 分別用來產生兩種PortletSession屬性:
… …
<portlet:actionURL windowState="NORMAL" portletMode="view" var="pu1">
<portlet:param name="ACTION" value="ApplicationScope"/>
</portlet:actionURL>
<portlet:actionURL windowState="NORMAL" portletMode="view" var="pu2">
<portlet:param name="ACTION" value="PortletScope"/>
</portlet:actionURL>
… …
4. JSP (jspShowSession.jsp)
取得以上創建的兩個PortletSession 的屬性值, 由Browser中可以看到只有Application Scope的Session 屬性可以被獲得.
if(ps.getAttribute("PortletSession.AS",PortletSession.APPLICATION_SCOPE)!=null){
app=ps.getAttribute("PortletSession.AS",PortletSession.APPLICATION_SCOPE).toString();
}
if(ps.getAttribute("PortletSession.PS",PortletSession.PORTLET_SCOPE)!=null){
portlet=ps.getAttribute("PortletSession.PS",PortletSession.PORTLET_SCOPE).toString();
}
以下代碼將Httpsession中的所有屬性全部列出:
可以看到PortletContainer是如何存取Portlet Scope類型的session屬性的.
<%
HttpSession s=request.getSession();
for (Enumeration e = s.getAttributeNames() ; e.hasMoreElements() ;) {
%><tr><td><%=e.nextElement()%></td></tr>
<%
}
%>
將以上源代碼編譯後, 再通過Eclipse生成/更新Portlet的web.xml後, 將所有配置及相關文件部署後, 啓動Tomcat.
在Browser中加載如下頁面: Http://localhost:8080/pluto/portal , 可以看到如下的頁面(圖:5-1)
圖:5-1
單擊PortletSession Example1 Page後可以看到如下兩個Portlet 頁面(圖5-2)
圖:5-2
上圖中上邊的一個Generate Portlet用來創建兩個PortletSession屬性,分別是Application Scope 和 Portlet Scope 類型.
下方的Display Portlet用以驗證Session的作用範圍.
單擊Application Scope Session, 可以看到如下頁面(圖5-3)
圖5-3
如上圖,生成了一個Application Scope的屬性, 名稱: PortletSession.AS
單擊Portlet Scope Session, 可以看到如下頁面(圖5-4)
圖5-5
生成了一個Portlet Scope Session屬性,所以Display portlet 無法取得. 但是從HttpSession 可以看到又創建了一個session屬性.
Case study: Session的共享
以上例子驗證了Portlet中Session的作用範圍, 可能在開發過程中經常需要有Popup頁面的處理這個時候就無法使用Portlet, 但是可以使用Servlet或者是JSP, 以下這個例子是一個Portlet和Servlet的Session共享的應用 (或許是Tomcat4的一個BUG), 我們將使用一個Portlet創建一個Application Scope的Session, 然後在一個Servlet中得到它.
1. Portlet (PortletSessionExample2_ShareSession.java)
以下代碼將創建一個application scope的Portlet Session:
… …
if(INF!=null){
PortletSession ps=request.getPortletSession();
ps.setAttribute("INF",INF,PortletSession.APPLICATION_SCOPE);
}
… …
2. JSP (jspLoginView.jsp)
… …
<%
PortletSession ps = renderRequest.getPortletSession();
String app=null;
if(ps.getAttribute("INF",PortletSession.APPLICATION_SCOPE)!=null){
app=ps.getAttribute("INF",PortletSession.APPLICATION_SCOPE).toString();
%> <u><a style="cursor:hand" οnclick='window.open("/pluto/popup","a","width=400,height=330,scrollbars=1")'>Click here to Popup</a></u> <%
}
%>
… …
3. Servlet (PopupServlet.java)
… …
HttpSession se=request.getSession();
Object info=se.getAttribute("INF");
… …
注: 由於Tomcat下webapps目錄下的pluto和portlets分別是兩個web application, 所以將以上的PopupServlet配置到pluto目錄下,而非portlets目錄下.
將以上源代碼編譯後, 再通過Eclipse生成/更新Portlet的web.xml後, 將所有配置及相關文件部署後, 啓動Tomcat.
在Browser中加載如下頁面:
Http://localhost:8080/pluto/portal , 可以看到如下的頁面
單擊PortletSession Example2 Page後可以看到如下頁面(圖5-6)
圖5-6
單擊 Click here to generate a session , 後可以看到如下頁面(圖5-7)
圖5-7
單擊 Click here to Popup , 後可以看到如下頁面(圖5-8)
圖5-8
• 源代碼及Portlet相關配置文件
A. Session Example1
1. Portlet (PortletSessionExample1_GenerateSesssion.java)
package portlets.portletsession;
/**
* @author terry
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
import javax.portlet.*;
import java.io.IOException;
public class PortletSessionExample1_GenerateSesssion extends GenericPortlet{
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException{
response.setContentType("text/html");
String jspName = getPortletConfig().getInitParameter("view");
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(jspName);
rd.include(request, response);
}
public void processAction(ActionRequest request,ActionResponse response)
throws PortletException,IOException{
String action=request.getParameter("ACTION");
if(action.equals("ApplicationScope")){
PortletSession ps=request.getPortletSession();
ps.setAttribute("PortletSession.AS",action,PortletSession.APPLICATION_SCOPE);
}else{
PortletSession ps=request.getPortletSession();
ps.setAttribute("PortletSession.PS",action,PortletSession.PORTLET_SCOPE);
}
}
}
2. Portlet (PortletSessionExample1_DisplaySession.java)
package portlets.portletsession;
/**
* @author terry
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
import javax.portlet.*;
import java.io.IOException;
public class PortletSessionExample1_DisplaySession extends GenericPortlet{
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html");
String jspName = getPortletConfig().getInitParameter("view");
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(jspName);
rd.include(request, response);
}
}
3. JSP (view_portletsession1_displaysession.jsp)
<%@ page session="false" %>
<%@ page import="javax.portlet.*"%>
<%@ page import="java.util.*"%>
<%@ taglib uri='/WEB-INF/tld/portlet.tld' prefix='portlet'%>
<portlet:defineObjects/>
<BR>
<h3>Display PortletSession and HttpSession Example</h3>
<%
PortletSession ps = renderRequest.getPortletSession();
String app="NULL";
String portlet="NULL";
if(ps.getAttribute("PortletSession.AS",PortletSession.APPLICATION_SCOPE)!=null){
app=ps.getAttribute("PortletSession.AS",PortletSession.APPLICATION_SCOPE).toString();
}
if(ps.getAttribute("PortletSession.PS",PortletSession.PORTLET_SCOPE)!=null){
portlet=ps.getAttribute("PortletSession.PS",PortletSession.PORTLET_SCOPE).toString();
}
%>
<b>PortletSession list:</b>
<table border=1>
<tr><td><b>Session Scope</b></td><td><b>Attribute Name</b></td><td><b>Attribute Value</b></td></tr>
<tr><td>Application scope</td><td>PortletSession.AS</td><td><b><%=app%></b></td></tr>
<tr><td>Portlet scope</td><td>PortletSession.PS</td><td><b><%=portlet%></b></td></tr>
</table>
<br>
<b>HttpSession list:</b>
<table border=1>
<tr><td><b>Attribute Name</b></td></tr>
<%
HttpSession s=request.getSession();
for (Enumeration e = s.getAttributeNames() ; e.hasMoreElements() ;) {
%><tr><td><%=e.nextElement()%></td></tr>
<%
}
%>
</table>
<BR><BR>
4. JSP (view_portletsession1_generatesession.jsp)
<%@ page session="false" %>
<%@ page import="javax.portlet.*"%>
<%@ page import="java.util.*"%>
<%@ taglib uri='/WEB-INF/tld/portlet.tld' prefix='portlet'%>
<portlet:defineObjects/>
<portlet:actionURL windowState="NORMAL" portletMode="view" var="pu1">
<portlet:param name="ACTION" value="ApplicationScope"/>
</portlet:actionURL>
<portlet:actionURL windowState="NORMAL" portletMode="view" var="pu2">
<portlet:param name="ACTION" value="PortletScope"/>
</portlet:actionURL>
<BR>
<h3>Generate PortletSession Example</h3>
<a href="<%=pu1%>">Application Scope Session</a>
<Br>
<a href="<%=pu2%>">Portlet Scope Session</a>
<Br><BR>
B. Session Example2
1.Portlet (PortletSessionExample2_ShareSession.java)
package portlets.portletsession;
/**
* @author terry
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
import javax.portlet.*;
import java.io.IOException;
public class PortletSessionExample2_ShareSession extends GenericPortlet{
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html");
String jspName = getPortletConfig().getInitParameter("view");
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(jspName);
rd.include(request, response);
}
public void processAction(ActionRequest request,
ActionResponse response)
throws PortletException,
java.io.IOException{
String INF=request.getParameter("INF");
if(INF!=null){
PortletSession ps=request.getPortletSession();
ps.setAttribute("INF",INF,PortletSession.APPLICATION_SCOPE);
}
}
}
2. Servlet (PopupServlet.java)
package servlet;
/**
* @author terry
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
import javax.servlet.http.*;
import javax.servlet.*;
import java.io.*;
public class PopupServlet extends HttpServlet{
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
PrintWriter pw=response.getWriter();
HttpSession se=request.getSession();
Object info=se.getAttribute("INF");
if(info!=null){
pw.println("<table border=1 bordercolor=black>");
pw.println(" <tr>");
pw.println(" <td><b>Attribute</b><td><td><b>Value</b></td> ");
pw.println(" </tr>");
pw.println(" <tr>");
pw.println(" <td>INF<td><td>"+ info.toString() +"</td> ");
pw.println(" </tr>");
pw.println("</table>");
}else{
pw.println("No session");
}
}
}
C. 配置文件
1. Portlet.xml
… …
<!-- PortletSession Example -->
<!-- PortletSession Example1 - GenerateSession -->
<portlet>
<description>PortletSession_Generator Example</description>
<portlet-name>PortletSession_GeneratorExample</portlet-name>
<display-name>PortletSession_Generator Example</display-name>
<portlet-class>portlets.portletsession.PortletSessionExample1_GenerateSesssion</portlet-class>
<init-param>
<name>view</name>
<value>/fragments/portletsession/view_portletsession1_generatesession.jsp</value>
</init-param>
<expiration-cache>-1</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<portlet-info>
<title>PortletSession_Generator Example</title>
<short-title>PortletSession_Generator</short-title>
<keywords>PortletSession_Generator</keywords>
</portlet-info>
</portlet>
<!-- PortletSession Example1 - DisplaySession -->
<portlet>
<description>PortletSession_Display Example</description>
<portlet-name>PortletSession_DisplayExample</portlet-name>
<display-name>PortletSession_Display Example</display-name>
<portlet-class>portlets.portletsession.PortletSessionExample1_DisplaySession</portlet-class>
<init-param>
<name>view</name>
<value>/fragments/portletsession/view_portletsession1_displaysession.jsp</value>
</init-param>
<expiration-cache>-1</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<portlet-info>
<title>PortletSession_Display Example</title>
<short-title>PortletSession_Display</short-title>
<keywords>PortletSession_Display</keywords>
</portlet-info>
</portlet>
… …
2. pageregistry.xml
… …
<!-- PortletSession Example1 Page -->
<fragment name="portletsessionpage1" type="page">
<navigation>
<title>PortletSession Example1 Page</title>
<description>PortletSession Example Page</description>
</navigation>
<fragment name="row1" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="10.40"/>
</fragment>
</fragment>
</fragment>
<fragment name="row2" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="10.50"/>
</fragment>
</fragment>
</fragment>
</fragment>
<!-- PortletSession Example2 Page -->
<fragment name="portletsessionpage2" type="page">
<navigation>
<title>PortletSession Example2 Page</title>
<description>PortletSession Example Page</description>
</navigation>
<fragment name="row1" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="10.60"/>
</fragment>
</fragment>
</fragment>
</fragment>
… …
3. Portletregiestry.xml
… …
<portlet id="40">
<definition-id>portlets.PortletSession_GeneratorExample</definition-id>
</portlet>
<portlet id="50">
<definition-id>portlets.PortletSession_DisplayExample</definition-id>
</portlet>
<portlet id="60">
<definition-id>portlets.PortletSession_ShareExample</definition-id>
</portlet>
… …
總結
Session對於Web 開發來説是非常重要且必要的元素, 對於Portlet來説, 其Session的功能和Servlet略有不同. 但實際還是HttpSession的延伸及包裝.
資源: • Pluto http://jakarta.apache.org/pluto
• Pluto Mail List http://news.gmane.org/gmane.comp.jakarta.pluto.user
• WSRP Spec1.0 http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsrp
• Apache的WSRP實現 (WSRP4J) http://ws.apache.org/wsrp4j/
• Apache’s Portal, JetSpeed: http://jakarta.apache.org/jetspeed/site/index.html
• JSR 168: http://www.jcp.org/en/jsr/detail?id=168
By Terry.liSpiritSeekerS@sqatester.com
本文使用本系列中Part1搭建的開發環境,如果沒有搭建好開發環境,請參考Portlet應用開發Part1進行開發環境的搭建.在Part1中我們已經介紹了Portlet的GenericPortlet類. 從形式上來看, Portlet與Servlet非常相似, 但是從request和response對象的具體特點及功能來説, 又有所不同. 本部分主要描述了Portlet的Request和Response對象的特點及其與Servlet的Request和Response對象的不同點.
Portlet的Request 對象
Portlet中的Request與Servlet的Request一樣接受Client端發送的Request, 但是與Servlet不同, Portlet的Request分為Action Request及Render Request兩種類型,因此Portlet接口中定義了兩種方法用來處理不同的Request. 分別是processAction(ActionRequest request,ActionResponse response) 和render(RenderRequest request,RenderResponse response),分別用以處理Action Request和Render Request. 某種意義上來講,render方法類似Servlet中的service方法,doView,doEdit,doHelp方法又類似doGet,doPost方法,如下圖:
1. RenderRequest和ActionRequest有什麼不同呢?
對於Portlet來説PortletRequest分為ActionRequest和RenderRequest兩種,分別是由renderURL和actionURL來觸發的.可以這樣理解, renderURL是actionURL的一種優化形式.Portlet開發過程中儘可能使用renderURL而避免使用actionURL. actionURL適用於有確實的Action(行為)的情況下. 比如説, form表單的遞交. Persistent狀態的改變,session的操作,preference的修改,這種情況下使用actionURL,而不使用renderURL, renderURL通常用來操作portlet內容的導航.
以下是兩個例子:
使用actionURL:
<%
PortletURL pu=renderResponse.createActionURL();
pu.setParameter("ACTION","LOGIN");
…
%>
<form name="usrfrm" method="post" action="<%=pu.toString()%>">
注: form表單遞交時,使用HTTP post方法,而不用get方法.因為某些Portal/Portlet Container的實現將內部狀態編碼到URL的Query字符串中.
使用renderURL:
<%
PortletURL pu=renderResponse.createRenderURL();
pu.setParameter("PAGE",Number);
…
%>
<a href=”<%=pu%>”>下一頁</a> 2. renderURL和actionURL的處理方式有什麼不同?
當客户端request是由一個renderURL觸發時,Portlet/Portlet Container會調用Portal頁面中所有Portlet的render方法. 如下: renderURL
/ | /
render render render 而當客户端request一個actionURL觸發時, Portlet/Portlet Container會先調用目標Portlet的processAction()方法, 當processAction方法處理完畢後,再分別調用Portal頁面中所有Portlet的render方法.如下:
actionURL
|
processAction
/ | /
render render render
由於以上原因,所以使用renderURL要比使用actionURL的performance來的好.
3. RenderRequest和ActionRequest的parameter參數作用範圍有什麼不同?
當客户端request一個actionURL觸發時,比如一個form表單的提交,所有的Parameter的get操作必須在processAction方法中進行. 例如:
JSP的form表單頁面:
<%
PortletURL pu=renderResponse.createActionURL();
pu.setParameter("ACTION","LOGIN");
…
%>
<form name="usrfrm" method="post" action="<%=pu.toString()%>">
… Portlet的處理:
public void processAction(ActionRequest req,ActionResponse res){
String str=req.getParameter(“ACTION”);
//response.setRenderParameter("ACTION",action);
} public void doView(ActionRequest req,ActionResponse res){
String str=req.getParameter(“ACTION”);
}
如上processAction方法中,getParamter方法將能成功得到表單中的參數ACTION所對應的值,因為我們知道,當目標portlet的processAction方法運行完後,Portlet Container將調用Portal頁面中所有Portlet的render方法.但是實際上doView方法中使用getParameter不會得到任何值.但是如果把processAction方法中註釋了的一行uncomment的話,你就可以在doView方法中的得到參數ACTION對應的值. 這説明action request的參數,render方法中不可以直接取到.必須使用了setRenderParameter方法,再次傳遞一次.
A case study
在這部分中,我們來做一個簡單的Portlet, 實現一個簡單的Form submit功能. 以下是代碼片段: (完整代碼請參考文章末尾)
JSP(view_portletrequest.jsp.jsp):
… …
<!-- Use PortletURL Object//-->
<%
PortletURL pu1=renderResponse.createActionURL();
pu1.setParameter("ACTION","Use PortletURL Object");
pu1.setPortletMode(PortletMode.VIEW);
%> <table width=100% border=0>
<TR><TD>1. Use PortletURL object to get an ActionURL and set current portlet mode to view</TD></TR>
<tr><td>
<form name="usrfrm" method="post" action="<%=pu1.toString()%>">
<input type=submit name=bt1 value="GetActionByJava">
</form>
<tr><td>
</table>
… …
注: 處理完form後將會將PortletMode設定為VIEW. 以上代碼使用了actionURL因為是form表單的遞交. 詳細請參考Portlet的Request 對象部分. 或者也可以使用Tag. 它同樣也可以生成一個Portlet的URL, 如下:
… …
<!-- Use Portlet Tag //-->
<portlet:actionURL windowState="maximized" portletMode="edit" var="pu2">
<portlet:param name="ACTION" value="Use Portlet Tag"/>
</portlet:actionURL> <BR>
<table width=100% border=0>
<TR><TD>2. Use Portlet Tag to get a ActionURL and and set current portlet mode to edit</TD></TR>
<tr><td>
<form name="usrfrm" method="post" action="<%=pu2%>">
<input type=submit name=bt2 value="GetActionByTag">
</form>
<tr><td>
</table>
… … 注: 它在處理完form後將會將PortletMode設定為EDIT,並且Window state會為最大化.
Portlet(PortletRequestExample.java):
… …
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException
{
String action=request.getParameter("ACTION");
System.out.println("ACTION" + action);
if(action==null){
action="";
}
response.setRenderParameter("ACTION",action);
}
… … JSP(view_portletrequest.jsp)
… …
<%
String getaction="";
if(request.getParameter("ACTION")!=null){
getaction=request.getParameter("ACTION");
}
%> <B>ACTION: <%=getaction%></B>
… …
JSP(edit_portletrequest.jsp.jsp)
… …
<%
String getaction="";
if(request.getParameter("ACTION")!=null){
getaction=request.getParameter("ACTION");
}
%>
<B>ACTION: <%=getaction%></B>
… …
將以上源代碼編譯後, 再通過Eclipse生成/更新Portlet的web.xml後, 將所有配置及相關文件部署後, 啓動Tomcat.
在Browser中加載如下頁面: Http://localhost:8080/pluto/portal , 可以看到如下的頁面(圖:4-1)
圖:4-1
單擊PortletRequest Example Page後可以看到如下Portlet 頁面(圖4-2)
圖:4-2
單擊 GetActionByJava 後, 得到如下(圖:4-3):
圖:4-3
單擊 GetActionByTag 後, 將跳轉到edit mode, 如下(圖:4-4):
圖:4-4
Portlet的Response 對象
與Request對象類似,Response對象也有兩種:分別是ActionResponse和RenderResponse, 分別封裝了對應ActionRequest和RenderRequest對象返回的所有信息。例如,重定向,windows state,portlet mode等的信息。其中他們的父類,PortletResponse擁有setProperty和addProperty方法,用以傳遞提供商指定的信息給portal/portlet container
1。ActionResponse和RenderResponse有什麼不同?
ActionResonse可以用來處理以下相關功能:
1) 重定向
sendRedirect方法用來進行幫助portal/portlet-container進行頭信息,及其內容的設定,並且將URL重定向到用户指定的頁面。
2) 改變windows state, portlet mode ,我們在以前章節中介紹了window state 和 portlet mode概念.
3) 傳遞Parameter參數到RenderRequest中去,如上面request部分中用到的例子。
RenderResponse用來提供如下功能(和Servlet中的Response更相似):
1) 設置ContentType
2) 得到Output Stream and Writer對象,用來產生頁面內容
3) Buffering
4) 設定Portlet的Title , 但必須先於portlet的輸出遞交前來調用,否則將會被忽略。(注:目前的pluto沒有實現,如果調用不會修改title)
A example
… …
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html");
PrintWriter pw=response.getWriter();
pw.print(“Hello, Portlet”);
… …
• 源代碼及Portlet相關配置文件 A. Portlet (PortletRequestExample.java)
package portlets.portletrequest;
/**
* @author terry
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
import javax.portlet.*;
import java.io.IOException; public class PortletRequestExample extends GenericPortlet{
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html"); String jspName = getPortletConfig().getInitParameter("view");
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(jspName); rd.include(request, response);
} public void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html"); String jspName = getPortletConfig().getInitParameter("edit");
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(jspName); rd.include(request, response);
} public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException
{
String action=request.getParameter("ACTION");
System.out.println("ACTION" + action);
if(action==null){
action="";
}
response.setRenderParameter("ACTION",action);
}
}
B. JSP (view_portletrequest.jsp)
<%@ page session="false" %>
<%@ page import="javax.portlet.*"%>
<%@ page import="java.util.*"%>
<%@ taglib uri='/WEB-INF/tld/portlet.tld' prefix='portlet'%>
<portlet:defineObjects/>
<BR>
<h3>Request Example</h3> <!-- Use PortletURL Object//-->
<%
PortletURL pu1=renderResponse.createActionURL();
pu1.setParameter("ACTION","Use PortletURL Object");
pu1.setPortletMode(PortletMode.VIEW);
%> <table width=100% border=0>
<TR><TD>1. Use PortletURL object to get an ActionURL and set current portlet mode to view</TD></TR>
<tr><td>
<form name="usrfrm" method="post" action="<%=pu1.toString()%>">
<input type=submit name=bt1 value="GetActionByJava">
</form>
<tr><td>
</table> <!-- Use Portlet Tag //-->
<portlet:actionURL windowState="maximized" portletMode="edit" var="pu2">
<portlet:param name="ACTION" value="Use Portlet Tag"/>
</portlet:actionURL> <BR>
<table width=100% border=0>
<TR><TD>2. Use Portlet Tag to get a ActionURL and and set current portlet mode to edit</TD></TR>
<tr><td>
<form name="usrfrm" method="post" action="<%=pu2%>">
<input type=submit name=bt2 value="GetActionByTag">
</form>
<tr><td>
</table> <%
String getaction="";
if(request.getParameter("ACTION")!=null){
getaction=request.getParameter("ACTION");
}
%> ACTION: <B><%=getaction%></B>
<BR><BR>
Current Portlet Mode: <B><big><%=renderRequest.getPortletMode()%></big></font></B><br> C. JSP (edit_portletrequest.jsp)
<%@ page session="false" %>
<%@ page import="javax.portlet.*"%>
<%@ page import="java.util.*"%>
<%@ taglib uri='/WEB-INF/tld/portlet.tld' prefix='portlet'%>
<portlet:defineObjects/>
<BR>
<h3>Request Example</h3> <%
String getaction="";
if(request.getParameter("ACTION")!=null){
getaction=request.getParameter("ACTION");
}
%> ACTION: <B><%=getaction%></B>
<BR><BR>
Current Portlet Mode: <big><B><%=renderRequest.getPortletMode()%></font></big></B><br>
D. Portlet.xml … …
<!-- PortletRequest Example -->
<portlet>
<description>PortletRequest Example</description>
<portlet-name>PortletRequestExample</portlet-name>
<display-name>PortletRequest Example</display-name>
<portlet-class>portlets.portletrequest.PortletRequestExample</portlet-class>
<init-param>
<name>view</name>
<value>/fragments/portletrequest/view_portletrequest.jsp</value>
</init-param>
<init-param>
<name>edit</name>
<value>/fragments/portletrequest/edit_portletrequest.jsp</value>
</init-param> <expiration-cache>-1</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
<portlet-mode>EDIT</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<portlet-info>
<title>PortletRequest Example</title>
<short-title>PortletRequest</short-title>
<keywords>PortletRequest</keywords>
</portlet-info>
</portlet>
… … E. portletentityregistry.xml
<?xml version="1.0" encoding="UTF-8"?>
<portlet-entity-registry>
<application id="10">
<definition-id>portlets</definition-id>
… …
<portlet id="30">
<definition-id>portlets.PortletRequestExample</definition-id>
</portlet>
… …
</application>
</portlet-entity-registry> F. pageregistry.xml
<?xml version="1.0"?>
<portal> <fragment name="navigation" class="org.apache.pluto.portalImpl.aggregation.navigation.TabNavigation">
</fragment> … …
<!-- PortletRequest Example Page -->
<fragment name="portletrequestpage" type="page">
<navigation>
<title>PortletRequest Example Page</title>
<description>PortletConfig Example Page</description>
</navigation>
<fragment name="row1" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="10.30"/>
</fragment>
</fragment>
</fragment>
</fragment>
… …
</portal>
注: web.xml文件可以從portlet.xml通過Eclipse的Plugin直接生成,所以沒有列出配置文件, 請參考本系列中的Part1.
資源:
• Pluto
http://jakarta.apache.org/pluto
• Pluto Mail List
http://news.gmane.org/gmane.comp.jakarta.pluto.user
• WSRP Spec1.0
http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsrp
• Apache的WSRP實現
http://ws.apache.org/wsrp4j/
• Apache’s Portal, JetSpeed:
http://jakarta.apache.org/jetspeed/site/index.html
• JSR 168:
http://www.jcp.org/en/jsr/detail?id=168• "Portlet 規範介紹" By Stefan Hepper 和 Stephan Hesmer
o Part 1: Get your feet wet with the specification's underlying terms and concepts (August 2003)
o Part 2: The Portlet API's reference implementation reveals its secrets (September 2003)
11:19:51 | 添加評論 | 發送消息 | 固定鏈接 | 查看引用通告 (0) | 寫入日誌 | 我的技術文章
By Jia.li(Terry.li)
SpiritSeekerS@sqatester.com
本部份將講述PortletConfig對象及其PortletContext對象的概念及應用.
• PortletConfig對象
和ServletConfig對象類似, PortletConfig對象提供Portlet初始的所需的參數及其對PortletContext對象存取提供相關方法.
和ServletConfig不同處在於, PortletConfig對象提供對Portlet Title Bar資源的I18N支持,我們可以設定不同的Resource Bundle文件用以提供多語言的支持, 如下portlet.xml文件:
… …
<portlet-info>
<title>PortletConfig Example</title>
<short-title>PortletConfig</short-title>
<keywords>PortletConfig</keywords>
</portlet-info>
… … 以上Portlet描述文件中的設置用於顯示Portlet的Title Bar文字, 同樣也可以使用Resource Bundle用以顯示Title Bar文字, 如下:
… …
<resource-bundle>
portlets.portletconfig.portletconfigexample
</resource-bundle>
… … • A case study
這裏我們將開發一個簡單使用Resource Bundle的Portlet.只需要添加所須的Resource Bundle文件.
我們使用英文及其中文的Resource Bundle, 如下:
3) Base Resource Bundle (portletconfigexample.properties)
# English Resource Bundle
#
# filename: portletconfigexample.properties
# Portlet Info resource bundle example
javax.portlet.title=PortletConfig Example
javax.portlet.short-title=PortletConfig
javax.portlet.keywords=PortletConfig 2) Chinese Resource Bundle (portletconfigexample_zh.properties)
# Chinese Resource Bundle
#
# filename: portletconfigexample.properties
# Portlet Info resource bundle example
javax.portlet.title=Portlet配置 例子
javax.portlet.short-title=Portlet配置
javax.portlet.keywords=Portlet配置 3) portlet.xml
… …
<resource-bundle>
portlets.portletconfig.portletconfigexample
</resource-bundle>
… …
• 源代碼及Portlet相關配置文件
1) Portlet (PortletConfigExample.java)
package portlets.portletconfig;
/**
* @author terry
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/ import javax.portlet.*;
import java.io.IOException;
import java.io.Writer; public class PortletConfigExample extends GenericPortlet{
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html");
String view = getPortletConfig().getInitParameter("view");
Writer writer=response.getWriter();
writer.write(view);
} public void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html");
String edit = getPortletConfig().getInitParameter("edit");
Writer writer=response.getWriter();
writer.write(edit);
}
} 2) Portlet.xml
… …
<!-- PortletConfig Example -->
<portlet>
<description>PortletConfig Example</description>
<portlet-name>PortletConfigExample</portlet-name>
<display-name>disPortletConfigExample</display-name>
<portlet-class>portlets.portletconfig.PortletConfigExample</portlet-class>
<init-param>
<name>view</name>
<value>Here is View Mode</value>
</init-param>
<init-param>
<name>edit</name>
<value>Here is Edit Mode</value>
</init-param>
<expiration-cache>-1</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
<portlet-mode>EDIT</portlet-mode>
</supports>
<supported-locale>zh</supported-locale>
<supported-locale>en</supported-locale>
<resource-bundle>portlets.portletconfig.portletconfigexample</resource-bundle>
</portlet>
… … 3) pageregistry.xml
… …
<!-- PortletConfig Example Page -->
<fragment name="portletconfigpage" type="page">
<navigation>
<title>PortletConfig Example Page</title>
<description>PortletConfig Example Page</description>
</navigation>
<fragment name="row1" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="10.20"/>
</fragment>
</fragment>
</fragment>
</fragment>
… … 4) PortletRegistry.xml
… …
<portlet id="20">
<definition-id>portlets.PortletConfigExample</definition-id>
</portlet>
… …
將以上源代碼編譯後, 再通過Eclipse生成/更新Portlet的web.xml後, 將所有配置及相關文件部署後, 啓動Tomcat.
在Browser中加載如下頁面: Http://localhost:8080/pluto/portal , 可以看到如下的頁面(圖:3-1)
如果機器的Locale及語言設定是以中文簡體為缺省,則單擊PortletConfig Example Page後可以看到如下Portlet 頁面(圖3-1):
圖3-1
注: 因為現在Pluto的開發中沒有做I18N的處理,這裏如果你的機器的Locale是中文的話, 顯示是亂碼,請將IE的encoding設定為GB2312(View -> Encoding -> Chinese Simplified), 如圖3-1.
如果機器的Locale及語言設定是英文的話, 將看到如下Portlet 頁面(圖3-2):
圖3-2
資源:• Pluto http://jakarta.apache.org/pluto
• Pluto Mail Listhttp://news.gmane.org/gmane.comp.jakarta.pluto.user
• WSRP Spec1.0http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsrp
• Apache的WSRP實現http://ws.apache.org/wsrp4j/
• Apache’s Portal, JetSpeed:http://jakarta.apache.org/jetspeed/site/index.html
• JSR 168: http://www.jcp.org/en/jsr/detail?id=168• "Portlet 規範介紹" By Stefan Hepper 和 Stephan Hesmer Part 1: Get your feet wet with the specification's underlying terms and concepts (August 2003)
Part 2: The Portlet API's reference implementation reveals its secrets (September 2003)
11:18:16 | 添加評論 | 發送消息 | 固定鏈接 | 查看引用通告 (0) | 寫入日誌 | 我的技術文章
By Jia.li(Terry.li)
SpiritSeekerS@sqatester.com
本部分使用本系列中Part1搭建的開發環境,如果還沒有搭建好開發環境,請參考Portlet應用開發(JSR168),Part1進行開發環境的搭建.
在Part1中,我們開發了一個簡單的portlet程序,但是幾乎所有的代碼和文件都是由Eclipse的pluto plugin替我們完成的,如何配置用來在Pluto中的Portal中添加新的Portal Page並且將新的Portlet添加到新的Page中去呢? Part2將一步一步教你如何創建一個新Portal Page.
• Portal的設定
Tomcat 中Webapps目錄: 其中pluto是portal所在目錄, 用以配置Portal Page的兩個文件分別是pageregistry.xml和portletentityregistry.xml (如圖Figure1_1)
Figure1_1
其中pageregistry.xml用來在Portal中配置Portal Page, 而portletentityregistry.xml 用來在Page中配置Portlet.
• portletentityregistry.xml
打開pageregistry.xml, 內容如下 : <?xml version="1.0" encoding="UTF-8"?>
<portlet-entity-registry>
<application id="1">
<definition-id>portlets</definition-id>
<portlet id="1">
<definition-id>portlets.SimplePortlet</definition-id>
</portlet>
</application>
</portlet-entity-registry> 其中application 標籤表明了目前的application 的 ID, portlet標籤定義了一個Portlet , 如果你新開發了一個Portlet , 可以加入以下內容:
<?xml version="1.0" encoding="UTF-8"?>
<portlet-entity-registry>
<application id="1">
<definition-id>portlets</definition-id>
<portlet id="1">
<definition-id>portlets.SimplePortlet</definition-id>
</portlet>
<portlet id="2">
<definition-id>portlets.IntroPortlet</definition-id>
</portlet>
</application>
</portlet-entity-registry> 這樣一來你又在portal中註冊了一個新的Portlet,接下來是將新註冊的Portlet加入Page中.
• pageregistry.xml
打開pageregistry.xml, 內容如下 : <?xml version="1.0"?>
<portal> <fragment name="navigation" class="org.apache.pluto.portalImpl.aggregation.navigation.TabNavigation">
</fragment>
<fragment name="sample" type="page">
<navigation>
<title>Sample Portlet</title>
<description>Basic page to show the simple portlet</description>
</navigation> <fragment name="row" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="1.1"/>
</fragment> </fragment>
</fragment>
</fragment>
</portal>
以上是sample portlet的配置.
Fragment標籤用來配置navigation(導航欄), Page,以及Page中的Column和Row.
分別用fragment標籤的type屬性來標識. Pluto自帶的Portal使用fragment標籤來配置及處理簡單的Portlet Layout.
<property name="portlet" value="1.1"/>中的value值是在portletentityregistry.xml中定義的. 分別對應application id 和portlet id, 將其值用”.”連接起來使用用以定義一個Portlet. 同樣 , 如果使用<property name="portlet" value="1.2"/>,則將使用portlets.IntroPortlet.
1) 配置同一行中的兩個Portlet, 如下:
<?xml version="1.0"?>
<portal> <fragment name="navigation" class="org.apache.pluto.portalImpl.aggregation.navigation.TabNavigation">
</fragment>
<fragment name="sample" type="page">
<navigation>
<title>Sample Portlet</title>
<description>Basic page to show the simple portlet</description>
</navigation> <fragment name="row" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="1.1"/>
</fragment> <fragment name="p2" type="portlet">
<property name="portlet" value="1.1"/>
</fragment> </fragment>
</fragment>
</fragment>
</portal>
2) 配置同一列中的兩個Portlet, 如下:
<?xml version="1.0"?>
<portal> <fragment name="navigation" class="org.apache.pluto.portalImpl.aggregation.navigation.TabNavigation">
</fragment>
<fragment name="sample" type="page">
<navigation>
<title>Sample Portlet</title>
<description>Basic page to show the simple portlet</description>
</navigation> <fragment name="row1" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="1.1"/>
</fragment> </fragment>
</fragment>
<fragment name="row2" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="1.1"/>
</fragment> </fragment>
</fragment>
</fragment>
</portal>
• Portlet Modes 和 Portlet window states
Portlet模式(Portlet Mode)是Portlet提供的用以區分 Portlet所執行功能的一個概念.通常情況下其擁有以下幾種模式:
1. VIEW
2. EDIT
3. HELP
以上各模式分別對應GenericPortlet中的 doView(…) , doEdit(…) , doHelp(…) 方法,分別調用以上方法來產生各個模式中的Fragment內容. 非常類似Servlet中的doGet(…) , doPost(…) 方法,都是Helper方法, 但是概念不同.
Portlet狀態(Portlet window states)提供了對於Portlet窗口的控制功能 , 其中有如下三種最基本的狀態:
1. Normal
2. Maximized
3. Minimized
Portlet開發人員可以在處理ActionRequest (以後的章節將講述其概念) 時使用代碼實現Portlet模式, 及其Portlet狀態的轉變.
注: 只能在處理ActionRequest時改變Portlet Modes和Portlet Window states.
以上Portlet Modes和Portlet window states都可以配置成custom Portlet mode和custom portlet window state. 不同的地方在於對於Mode來説, 定製化的Mode需要對GenericPortlet的doDispatch方法進行重寫(Overriding), 因為GenericPortlet類通過render方法按照不同的Portlet Mode將request分別分發給doView,doEdit,doHelp等輔助方法. 如果需要定製的Mode , 必須重寫doDispatch 方法. 同時如果使用Portal提供商的Portlet Modes 或者 Portlet window States,都必須在部署描述中添加相關的設定.
A. 添加Custom Portlet Modes
以下使用代碼實現了一個新的Portlet Mode: CONFIG , 當然也可以使用Portal提供商支持的Portlet Mode. 但是需要Mapping到Portal提供商所支持的Portlet Mode. 使用如下描述:
<custom-portlet-mode>
<description>Provides administration functions</description>
<portlet-mode>CONFIG</portlet-mode>
</custom-portlet-mode> 1. Portlet代碼 (CustomPortletModeExample.java)
package portlets.portletmode;
/**
* @author terry
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
import javax.portlet.*;
import java.io.IOException; public class CustomPortletModeExample extends GenericPortlet{
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html"); String jspName = getPortletConfig().getInitParameter("all");
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(jspName); rd.include(request, response);
} public void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html"); String jspName = getPortletConfig().getInitParameter("all");
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(jspName); rd.include(request, response);
}
public void render(RenderRequest request, RenderResponse response)
throws PortletException, IOException{
doDispatcher(request,response);
}
public void doDispatcher(RenderRequest request, RenderResponse response)
throws PortletException, IOException{
if(!request.getWindowState().equals(WindowState.MINIMIZED)){
if(request.getPortletMode().equals(PortletMode.VIEW)){
doView(request,response);
}else if(request.getPortletMode().equals(PortletMode.EDIT)){
doEdit(request,response);
}else if(request.getPortletMode().equals(new PortletMode("CONFIG"))){
doConfig(request,response);
}
}
}
public void doConfig(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
response.setContentType("text/html"); String jspName = getPortletConfig().getInitParameter("all");
PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(jspName); rd.include(request, response);
}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException
{
String action=request.getParameter("ACTION");
System.out.println("ACTION" + action);
if(action==null){
action="";
}
response.setRenderParameter("ACTION",action);
}
} 2. JSP(all_mode.jsp)
<%@ page session="false" %>
<%@ page import="javax.portlet.*"%>
<%@ page import="java.util.*"%>
<%@ taglib uri='/WEB-INF/tld/portlet.tld' prefix='portlet'%>
<portlet:defineObjects/>
<br>
<h3>Custom Portlet Mode Example</h3>
Current Portlet Mode: <b><%=renderRequest.getPortletMode()%></b><br>
Current Window State: <b><%=renderRequest.getWindowState()%></b><br>
<br>
5. Portlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="/org/apache/pluto/portalImpl/xml/portlet-app_1_0.xsd" version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="/org/apache/pluto/portalImpl/xml/portlet-app_1_0.xsd /org/apache/pluto/portalImpl/xml/portlet-app_1_0.xsd"> <!-- Custom PortletMode Example -->
<portlet>
<description>CustomPortletModeExample Example</description>
<portlet-name>CustomPortletModeExample</portlet-name>
<display-name>CustomPortletModeExample</display-name>
<portlet-class>portlets.portletmode.CustomPortletModeExample</portlet-class>
<init-param>
<name>all</name>
<value>/fragments/portletmode/all_mode.jsp</value>
</init-param>
<expiration-cache>-1</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
<portlet-mode>EDIT</portlet-mode>
<portlet-mode>CONFIG</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<portlet-info>
<title>Custom PortletMode Example</title>
<short-title>PortletMode</short-title>
<keywords>PortletMode</keywords>
</portlet-info>
</portlet> </portlet-app>
6. portletregistry.xml.
<?xml version="1.0" encoding="UTF-8"?>
<portlet-entity-registry>
<application id="10">
<definition-id>portlets</definition-id>
<portlet id="10">
<definition-id>portlets.CustomPortletModeExample</definition-id>
</portlet>
</application>
</portlet-entity-registry> 7. pageregistry.xml
<?xml version="1.0"?>
<portal> <fragment name="navigation" class="org.apache.pluto.portalImpl.aggregation.navigation.TabNavigation">
</fragment>
<!-- Custom PortletMode Example Page -->
<fragment name="customportletmodepage" type="page">
<navigation>
<title>Custom PortletMode Example Page</title>
<description>Custom PortletMode Example Page</description>
</navigation>
<fragment name="row1" type="row">
<fragment name="col1" type="column">
<fragment name="p1" type="portlet">
<property name="portlet" value="10.10"/>
</fragment>
</fragment>
</fragment>
</fragment> </portal>
將以上源代碼編譯後, 再通過Eclipse生成/更新Portlet的web.xml後, 將所有配置及相關文件部署後, 啓動Tomcat.
在Browser中加載如下頁面: Http://localhost:8080/pluto/portal , 可以看到如下的頁面(圖:2_1)
圖2-1
單擊Custom PortletMode Example Page後可以看到如下Portlet 頁面:
圖2-2
單擊右上角的config後,可以看到如下頁面片段:
圖2-3
以上可以看到新添加的Portlet Mode: config.
B. 添加Custom Window States
添加新的window states同樣需要在配置文件中加入相應的描述,如下:
<custom-window-state>
<description>Occupies 50% of the portal page</description>
<name>half_page</name>
</custom-window-state>
但是同樣需要Mapping到Portal提供商所支持的Window state.
總結:
因為Eclipse 的Pluto plugin使用的是version1.0 , 所以可能有許多Bugs或者沒有實現的功能, 所以如果需要最新版本的Pluto , 可以在Apache的CVS上下載, 下載的代碼中有現成的Build scripts 以及相關的deploy script.請參考相關文檔資料.
資源:
• Pluto
http://jakarta.apache.org/pluto
• Pluto Mail List
http://news.gmane.org/gmane.comp.jakarta.pluto.user
• WSRP Spec1.0
http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsrp
• Apache的WSRP實現
http://ws.apache.org/wsrp4j/
• Apache’s Portal, JetSpeed:
http://jakarta.apache.org/jetspeed/site/index.html
• JSR 168:
http://www.jcp.org/en/jsr/detail?id=168• "Portlet 規範介紹" By Stefan Hepper 和 Stephan Hesmer
Part 1: Get your feet wet with the specification's underlying terms and concepts (August 2003)
Part 2: The Portlet API's reference implementation reveals its secrets (September 2003)
Java Portlet應用開發 (JSR168), Part 1
By Jia.li (Terry.li)
SpiritSeekerS@sqatester.com
本系列將介紹如何開發基於Portlet Specification v1.0 (JSR168) 的Portlet應用程序以及相關的概念. 這裏使用Pluto v1.0作為Portal/Portlet Container. 並且可以將開發完成的Portlet應用程序發佈到任何遵循JSR168規範的其他Portlet Container和Portal Server上.
• 為什麼要發佈Portlet Specification? 什麼是JSR168?
越來越多的公司開發了各自的Portal組件和基於其的Portal產品(如Bea, IBM, Oracle, Sun, Sybase, Vignette, Novell, SAP, Plumtree, Apache 等.這種互不兼容的接口實現帶給軟件開發商以及Web開發人員各種問題, 為了解決這些問題, JCP發佈了JSP168 (Java Specification Request), Portlet Specification v1.0, 用以提供不同Portal和Portlets的實現之間的互通性.可能許多軟件開發商 (如上所列) 提供更為強大的Portlet實現, 但是如果希望開發人員希望所開發的Portlet程序能夠不依賴於某一種或者幾種平台, 那麼使用JSR168 Portlet毫無疑問是你的首選.
• 什麼是Portal?
Portal是基於WEB的應用程序, 一個信息平台, 它將不同來源的各種資源進行整合並集中展現給客户. 通常其有如下三個特點:
a. Personalization (個性化)
b. Single sign on (單點登陸)
c. Content aggregation (內容聚合)
Personalization是Portal提供的特性之一, 用來提供用户的個性化設置. Single sign on是J2EE的一個特性.其中Content aggregation是Portal比較有特色的特性, 它將不同來源的信息整合到一個同一個頁面中, 使得用户可以更便捷, 更快速的進行某些商業應用. 這裏舉一個簡單的商業應用的例子, 如果某一客户需要進行一次商業採購行為,以往需要訪問不同的產品供應商的主頁得到相關信息,這往往是一個耗時耗力的過程, 但如果使用Portal將所有經常使用的相關商品供應商的商品瀏頁面都整合到一個Portal頁面中, 那麼所有的供應商的商品都可以更快的被瀏覽,篩選, 加快了客户的商業運作效率.
• 什麼是Portlet?
Portlet是一種基於WEB組件的JAVA技術, 由Portlet Container進行管理. 處理請求並動態返回頁面, 可以作為Portal的可即插即用的界面組件.
• 什麼是Portlet Container?
Portlet Container用來管理Portlet的生命週期並且提供其運行所需要的必要環境. 並且給Portlet Preferences提供持久性(Persistent)存取服務.但是其不支持Portlet的aggregation(內容聚合). 內容聚合由Portal組件提供.這個概念需要弄清楚. 注: Portlet Preferences是Portlet的一個新特性,提供類似數據庫的功能.但是不是用來取代數據庫. 只能用來存取簡單的Portlet配置參數.
• 什麼是WSRP?
WSRP 是 OASIS Web Service for Remote Portlets的縮寫. WSRP是Web service的一種新的商業應用, 一種新的標準, 主要用來簡化Portal對於各種資源或者程序整合的複雜性, 可以避免編程帶來的整合麻煩和問題. 而且Portal的管理員可以從海量的WSRP服務中選擇需要的功能用以整和到目前使用的Portal中.
它有三種Roles:
1) Producer -> 提供Portlet
2) Consumer -> 使用Portlet
3) End User -> 最終用户
它的特點在於Producer將Consumer所需要的信息通過WSRP返回給Consumer,這些信息是相對的標記Fragment(片段),例如HTML,XHTML等, 直接可以鑲入用户的Page中,而不用象Web service一樣需要單獨開發用户端接口. 再舉個WSRP的商業應用的例子: 如果一個客户需要採購一些PC軟件,那麼這個客户通過互聯網登陸某家PC軟件代理供應商的網頁,查詢相關信息,但是做為軟件代理公司,PC軟件的高級使用指南及配置工具是非常缺乏或者是無法及時更新的,這樣一來使得用户需要登陸軟件開發商的站點上才能得到相關信息. 然後再回到代理商主頁進行訂購. 而比較理想的商業應用應該是代理公司可以整合軟件開發商提供的使用指南或者配置工具(可以由開發商及時更新).但是如果使用XML API, 那麼需要針對不同軟件提供商開發不同的接口實現. 而使用WSRP可以將相關的信息及工具直接鑲入到代理商的頁面用以動態,及時提供給客户. WSRP4J是Apache的WSRP標準實現, 請參考文章末尾的資源部分.
• Portlet and Servlet 比較 摘自(Portlet Specs v1.0)
相同點:
- Portlets are Java technology based web components
- Portlets are managed by a specialized container
- Portlets generate dynamic content
- Portlets lifecycle is managed by a container
- Portlets interact with web client via a request/response paradigm
不同點:
- Portlets only generate markup fragments, not complete documents. The Portal aggregates portlet markup fragments into a complete portal page
- Portlets are not directly bound to a URL
- Web clients interact with portlets through a portal system
- Portlets have a more refined request handling, action requests and render requests
- Portlets have predefined portlet modes and window states that indicate the function the portlet is performing and the amount of real state in the portal page
- Portlets can exist many times in a portal page
Portlet特有:
Portlets have means for accessing and storing persistent configuration and customization data Portlets have access to user profile information
Portlets have URL rewriting functions for creating hyperlinks within their content, which allow portal server agnostic creation of links and actions in page fragments
Portlets can store transient data in the portlet session in two different scopes: the application-wide scope and the portlet private scope
Servlet特有:
Setting the character set encoding of the response
Setting HTTP headers on the response
The URL of the client request to the portal
• 什麼是Pluto ?
Pluto 是 Apache 的一個Open Source項目, 是基於Portlet Spec (v1.0) (JSR168) 的一個 Portlet Container 的實現. 它也提供了 一個演示版的Sample Portal實現,用以進行Portlet Container的測試. 但是功能相對簡單: 例如, 1) 沒有複雜的Layout實現. 2) 不是multi-user enabled, 比如, 不同User之間的Portlet Preferences互相是 可以share的. 請記住Pluto只是一個Portlet Container的實現, 不是一個Portal的實現. 如果你需要功能更為強大的Portal,可以使用JetSpeed或者其他功能更強大的Portal , 它同樣也是Apache的一個Open Source Project. 請參考文章末尾的資源部分.
• 概念 如圖: Figure 1.1
a. Decorations and controls (修飾部分及 控制部分)
b. Portlet fragment (Portlet 片段)
c. Portlet window (Portlet 窗口)
d. Portlet page (Portlet 頁面)
Figure 1.1
• 開發工具( Eclipse2.1, Pluto-plugin, Jakarta-tomcat-4.1.29)
下載地址:
Eclipse
http://www.eclipse.org/downloads/index.php
Ant
http://prdownloads.sourceforge.net/plutoeclipse/org.eclipsefan.pluto.ui_1.0.0.zip?download
Pluto
http://
JDK1.4
http://Java.sun.com
• 配置開發環境
希望這編文章可以幫大家搭建一個簡單的Portlet開發環境, 熟悉Portlet相關的知識.
資源:
• Pluto
http://jakarta.apache.org/pluto
• Pluto Mail List
http://news.gmane.org/gmane.comp.jakarta.pluto.user
• WSRP Spec1.0
http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wsrp
• Apache的WSRP實現 (WSRP4J)
http://ws.apache.org/wsrp4j/
• Apache’s Portal, JetSpeed
http://jakarta.apache.org/jetspeed/site/index.html
• JSR 168
http://www.jcp.org/en/jsr/detail?id=168
• "Portlet 規範介紹" By Stefan Hepper 和 Stephan Hesmer
- Part 1: Get your feet wet with the specification's underlying terms and concepts (August 2003)
- Part 2: The Portlet API's reference implementation reveals its secrets (September 2003)