扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
引言
从 6.1 版开始,WebSphere Application Server 提供了嵌入式 JSR168 portlet 容器。您现在可以将 Portlet 部署到 Application Server V6.1,而以前只能将其部署到门户服务器(如 IBM WebSphere Portal,以下称为 WebSphere Portal)。
此功能具有以下几个主要优点:
不过,Application Server 6.1 不能提供通常内置到门户服务器(如 WebSphere Portal)的扩展特性和功能。门户的关键特性是其可自定义的外观。在门户服务器中,通常通过配置来动态处理自定义和个性化。例如,您可以基于用户组动态地个性化页面主题和皮肤,并且您可以分配特定的用户组权限,以便在运行时使用 Portlet。如果希望在 Application Server 中使用这些特性,则必须亲自编写它们。另外,Application Server 中的聚合是使用聚合标记库以编程方式完成的。如果您希望在一个页面中显示多个 Portlet,则需要使用聚合标记库编写代码才能做到这一点。(请参阅参考资料中列出的系列文章“探索 WebSphere Application Server V6.1 Portlet 容器”)。
Tile 框架提供定义普通布局的机制,以便在门户中重新用作主题模板。您可以合并 Tile(作为主题模板)和 Portlet 聚合(作为页面内容),以便在 Application Server 中快速实现门户外观。可以使用 Tile 标记库定义页面布局(主题),以使用 Portlet 聚合标记库组装 Portlet。然后,将聚合 Portlet 链接到页面布局中的 Tile。Tile 和 portlet 标记都可以采用参数,所以您可创建自定义外观和动态内容。
本文向您介绍如何在 Application Server 中集成 Struts Tiles 标记和 Portlet 聚合标记,以创建自定义门户页面和门户外观。您将了解如何:
示例代码仅供演示使用。不过,您可以使用本文提供的技术,为自己的应用程序实现面向更多客户的外观。读者应具备 Application Server V6.1 中 Tile、Portlet 和 Portlet 框架的基本知识,并知道如何创建和使用它们。本文中使用的 Portlet 来自 IBM 和 Sun 的示例 JSR 168 Portlet 代码(请参阅参考资料)。在 WebSphere Application Server Toolkit 6.1 中重新打包示例 Portlet,以便将其部署到 Application Server。
|
将 Tile 和 Portlet Aggregation Framework 一起使用
例如,让我们查看一下经典门户布局,它由标头区域、菜单区域、正文区域和页脚区域组成(图 1)。您可以将每个区域当作其内容可以作为参数(页面、页面段落和字符串)传递的 Tile。可以分别在菜单布局和正文布局中将菜单区域和正文区域定义为模板,如图 1 所示。菜单布局由动态页面组成,每个页面链接一组在 Portlet 布局中聚合的不同 Portlet。您可以按特定用途将若干子页面组合到一个页面。正文区域中的 Portlet 布局由不同数量的 Portlet 组成,这些 Portlet 是使用 Application Server 提供的 Portlet 聚合标记聚合的。
在 Application Server 6.1 中,您可以使用以下两种方法创建独特的门户外观。
例如,您可能需要一个顶部横排(而不是自顶向下)、基于树的页面导航菜单。可以始终创建多个布局,以供不同的页面和不同的用户组使用。例如,您可能希望管理门户页面来使用不同的页面、菜单和 Portlet 布局,以将该界面与用户界面区分开来。
图 1. 经典门户布局
|
创建门户布局
以下各部分向您介绍如何使用 Tile 和 Portlet 聚合标记分别创建页面、菜单和 Portlet 布局。
创建页面布局
在户门中,页面布局提供所有页面的一般主题。门户中的每个页面都将调用页面布局,并将参数(页面或页面段落)传递到在页面布局中定义的 Tile。清单 1 显示了经典页面布局,它使用 <tiles:insert />
标记在表的不同区域中插入了五个 Tile(标题、标头、菜单、正文和页脚),如图 1 所示。Tile 占用的区域将由调用 JSP 页面传入的参数填充。
清单 1. 包括每个区域 Tile 的经典页面布局
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <tiles:importAttribute /> <html> <head> <logic:present name="title"> <title><tiles:getAsString name="title" ignore="true"/></title> </logic:present> </head> <body> <table border="0" cellspacing="0" cellpadding="0" width="100%" bgcolor="#F8FBFE"> <tr> <td align="left" colspan="2"> <tiles:insert attribute="header" ignore="true"> <tiles:put name="title" beanName="title" beanScope="tile"/> </tiles:insert> </td> </tr> <tr><td> </td></tr> <tr> <td valign="top"> <ul> <tiles:insert attribute="menu" /> </ul> </td> <td> <div align="center"> <tiles:insert attribute="body" /> </div> </td> </tr> </table> <tiles:insert attribute="footer" ignore="true"/> </body> </html> |
创建页面导航
门户菜单显示当前登录到门户的用户可以查看的页面列表。清单 2 展示了如何对菜单布局进行编码,以显示页面名称和链接的列表。每个链接引用一组在门户正文中聚合的 Portlet。
清单 2. 门户页面菜单布局
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <tiles:useAttribute id="items" name="items" classname="java.util.List" /> <logic:iterate id="item" name="items" type="org.apache.struts.tiles.beans.SimpleMenuItem"> <bean:define id="link" name="item" property="link" type="java.lang.String"/> <logic:match name="link" location="start" value="/" > <A href="<%=link%>" > <bean:write name="item" property="value"/> </A> </logic:match> <logic:notMatch name="link" location="start" value="/" > <A href="<%=request.getContextPath()%>/<%=link%>"> <bean:write name="item" property="value"/> </A> </logic:notMatch> <P/> </logic:iterate> |
清单 2 中的菜单布局使用 Struts 逻辑标记以迭代方式显示页面名称和链接。通过调用 JSP 页面将页面及其链接传入 Bean (org.apache.struts.tiles.beans.SimpleMenuItem) 的列表 (java.util.List
)。 如果传入 Bean 的链接引用绝对 URL(以“/
”开头),则该链接会复制过去并显示。否则,该链接会附加到使用 request.getContextPath() 获得的页面上下文,然后显示。
创建 portlet 布局
门户正文在不同的列和行中都包含 Portlet。Portlet 布局为门户正文区域中的每个 Portlet 定义单元网格。清单 3 显示了 Portlet 布局的编码,该编码显示正文区域中 Portlet 放置的表格视图。在该布局中,门户正文中的列数是作为字符串从调用 JSP 传递的。Portlet URL、每个 Portlet 的支持模式和最初的 Portlet 模式都从调用 JSP 页面传入实用程序 Bean 的列表 (com.jktelecom.PortletInfo
)。因此,调用 JSP 页面可以对页面中 Portlet 的数量和放置、初始模式和每个 Portlet 的支持模式进行总体控制,所以您不必修改每个 Portlet 的 Portlet 布局。
Portlet 布局使用 Portlet 列表的列数和大小(二者均由调用 JSP 页面传递)来确定呈现 Portlet 的行数和单元。Portlet 布局按最初插入到列表的顺序遍历列表。它使用 <portlet:state>
标记放置 Portlet 支持的模式,并使用 <portlet:insert>
标记放置 Portlet URI,所以模式和 Portlet 内容都可以动态地呈现给相应的单元。这将显示 Portlet 模式和适当的链接,以及 <portlet:insert>
标记从 Portlet 部署描述符 (portlet.xml) 检索的 Portlet 标题。
清单 3. Portlet 布局
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<%@ taglib uri="http://ibm.com/portlet/aggregation" prefix="portlet" %>
<%@ page import="java.util.*" %>
<%@ page import="com.jktelecom.*" %>
<%@ page isELIgnored ="false" %>
<%
String page_uri = (String)session.getAttribute("Current_Page");
String uri_prefix = PageInfo.getPageName(page_uri) +"/";
%>
<portlet:init portletURLPrefix="<%=uri_prefix%>" >
<tiles:importAttribute />
<table border="1">
<%-- Prepare the links list to be iterated --%>
<bean:define id="portlets" name="portlets" type="java.util.List" />
<bean:define id="numColumns" name="numColumns" type="java.lang.String" />
<%
int cols = Integer.parseInt(numColumns);
Iterator loop = portlets.iterator();
int portlet_count = 0;
while (loop.hasNext()) {
%>
<!-- create portal table with number columns defined by variable cols -->
<tr bgcolor="#e0eaf8">
<%
String[] portRefs = new String[cols];
for (int i=0; i < cols; i++) {
if (!loop.hasNext()) {
break;
}
PortletInfo portletInfo = (PortletInfo)loop.next();
portRefs[i] = portletInfo.getName();
String windowId = String.valueOf(portlet_count);
%>
<!-- insert portlet title bar -->
<td>
<table width="100%">
<tr>
<td>
<b><span id="title_<%=portlet_count%>">Portlet <%=
portlet_count%></span></b>
</td>
<td align="right">
<%
String portletName = portletInfo.getName();
String[] portletModes = portletInfo.getSupportedModes(portletName);
for (int j=0; j < portletModes.length; j++ ) {
%>
<a href="<portlet:state url='<%=portletName%>'
windowId='<%=windowId%>'
portletMode='<%=portletModes[j]%>' />">
<%=portletModes[j]%> </a>
<%
}
%>
</td>
</tr>
</table>
</td>
<%
portlet_count++;
}
%>
</tr>
<tr>
<%
for (int k=0; k < cols; k++) {
int title_num = portlet_count-cols+k;
String windowId = String.valueOf(title_num);
%>
<td>
<!-- insert portlet -->
<portlet:insert url="<%=portRefs[k]%>" windowId="<%=windowId%>"
titleVar="title" />
</td>
<!-- insert portlet title -->
<script type="text/javascript">
document.getElementById("title_<%=title_num%>").firstChild.nodeValue = "${title}";
</script>
<%
}
%>
</tr>
<tr><td colspan="<%=cols%>"> </td></tr>
<%
}
%>
</table>
</portlet:init>
|
在 Portlet 布局中,您需要解决以下问题,如清单 3 所示。
设置 URL 前缀
您需要在 init
Portlet 聚合标记中为 portletUrlPrefix
属性指定适当的 URL 前缀。
该前缀用于由 state
和 insert
标记创建的 Portlet URL。它由以下两个部分组成:聚合上下文和聚合器映射。如果您使用 URL 的相对路径,则 Portlet 容器将为您管理聚合上下文。不过,如果使用 URL 的绝对路径(例如 /jkTelecom/Tools/
),则在将门户应用程序 (WAR) 部署到 Application Server 时,必须指定您在聚合上下文中使用的上下文根 (jkTelecom
)。您必须在 Web 应用程序部署描述符 (web.xml) 中,将 URL 中的聚合映射部分配置为指向显示 Portlet 聚合(Portlet 布局)的当前页面引用的 URL。
例如,如果引用 tools.jsp
的 Tools 页面调用当前 Portlet 聚合,则 Portlet URL 必须映射到 tools.jsp,下面的门户配置部分将介绍关于映射的详细信息。
要设置适当的聚合器映射,您必须跟踪用户当前查看的页面,并相应地设置聚合映射。示例代码按以下方式处理跟踪:
<% String current_page = "tools.jsp"; session.setAttribute("Current_Page", current_page); %> |
<% String page_uri = (String)session.getAttribute("Current_Page"); String uri_prefix = PageInfo.getPageName(page_uri) +"/"; %> |
清单 4. 页面映射
static HashMap pages; static { pages = new HashMap(); pages.put("index.jsp", "Home"); pages.put("account.jsp", "Account"); pages.put("tools.jsp", "Tools"); pages.put("reports.jsp", "Report"); pages.put("services.jsp", "Services"); ... } public static String getPageName(String uri) { return (String)pages.get(uri); } |
<portlet:init portletURLPrefix="<%=uri_prefix%>" |
处理 Portlet 模式
每个 Portlet 通常支持不同的模式(如视图、编辑和帮助),其图标和 Portlet 标题通常一起显示在门户中。在显示图标之前,您必须知道 Portlet 支持什么模式。否则,支持模式将从 Portlet 丢失(如果您不显示该模式的图标),或抛出异常(如果 Portlet 不支持显示的模式)。
Portlet 部署描述符的 <supports>
部分提供了 Portlet 模式信息(清单 5)。
清单 5. Portlet 部署描述符 (portlet.xml) 片段
<supports> <mime-type>text/html</mime-type> <portlet-mode>config</portlet-mode> <portlet-mode>edit</portlet-mode> <portlet-mode>help</portlet-mode> </supports> |
与使用 Portlet 聚合标记 <portlet:insert>
检索 Portlet 标题不一样,该代码不提供任何 Portlet 聚合标记帮助您检索支持模式。您必须亲自查看 Portlet 部署描述符才能检索支持模式。示例代码中使用的所有 Portlet 的支持模式都存储在散列图中,所以可以动态地检索它们(清单 6):
清单 6. 支持的 Portlet 模式
static HashMap supportedModes; static { supportedModes = new HashMap(); supportedModes.put("IBMSamples/HelloWorld", new String[]{"view"}); supportedModes.put("IBMSamples/WorldClock", new String[]{"view", "edit", "help"}); supportedModes.put("IBMSamples/SQLQuery", new String[]{"view"}); supportedModes.put("IBMSamples/HelloJSP", new String[]{"view"}); supportedModes.put("SunSamples/JSPPortlet", new String[]{"edit", "help"}); supportedModes.put("SunSamples/NotepadPortlet", new String[]{"edit", "help"}); supportedModes.put("SunSamples/BookmarkPortlet", new String[]{"edit", "help"}); } public String[] getSupportedModes(String portletName) { return (String[])supportedModes.get(portletName); } |
|
组装门户
在本部分中,您将了解如何编写 JSP,以便使用页面、菜单和 Portlet 布局创建适当的门户页面。您还将了解如何将 Web 应用程序描述符 (web.xml) 配置为支持 Portlet 聚合映射。
使用页面布局
调用 JSP 页面可传递插入到页面布局的参数。参数可以是另一个 JSP 页面、页面段落或简单的字符串,具体取决于当前用户和显示给用户的内容。
清单 7 显示了缺省页面布局定义的代码,该代码指定传递到页面布局的缺省参数。例如,将 header.jsp 文件传递到页面布局中的页眉 Tile,将 footer.jsp 文件传递到页面布局中的页脚 Tile。
清单 7. 缺省页面布局定义
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <logic:notPresent name="pageLayoutDef" scope="request" > <tiles:definition id="pageLayoutDef" page="/pageLayout.jsp" scope="request" > <tiles:put name="title" type="string" value="jkTelecom Portal" /> <tiles:put name="header" value="/header.jsp" /> <tiles:put name="footer" value="/footer.jsp" /> <tiles:put name="body" value="/default_body.jsp" /> <tiles:put name="menu" value="/default_menu.jsp" /> </tiles:definition> </logic:notPresent> |
通过传递不同的参数调用 JSP 页面还可以覆盖在页面布局定义中定义的缺省行为。在清单 8 中,调用 JSP 页面 (tools.jsp) 使用清单 7 中的页面布局定义;它使用 menu.jsp 和 portlets.jsp 覆盖缺省菜单页面 (default_body.jsp) 和正文页面 (default_menu.jsp)。
清单 8. 使用页面布局的门户页面 (tools.jsp)
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <% String current_page = "tools.jsp"; session.setAttribute("Current_Page", current_page); %> <jsp:include page="/pageLayoutDef.jsp" /> <tiles:insert beanName="pageLayoutDef" flush="true" beanScope="request"> <tiles:put name="body" value="/portlets.jsp" /> <tiles:put name="menu" value="/menu.jsp" /> </tiles:insert> |
使用页面导航
清单 9 显示了使用菜单布局的门户菜单 (menu.jsp)。门户菜单使用 jsp:useBean
创建 SimpleMenuItem
的实例。然后使用 jsp:setProperty
设置 SimpleMenuItem
Bean 的 link
属性和 value
属性。最后,使用 tiles:add
将 Bean 添加到列表。
为了创建基于角色的页面,可以在用户组的基础上自定义菜单,而无需修改菜单布局。因此,仅当用户是特定的角色,并具有访问页面的权限时,页面才可视。用户角色是通过请求对象的 isUserInRole 方法确定的。
清单 9. 使用菜单布局的门户面页菜单
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <tiles:importAttribute /> <tiles:insert page="/menuLayout.jsp" flush="false" > <tiles:putList name="items" > <jsp:useBean id="item_0" class="org.apache.struts.tiles.beans.SimpleMenuItem" /> <jsp:setProperty name="item_0" property="link" value="index.jsp" /> <jsp:setProperty name="item_0" property="value" value="Home" /> <tiles:add beanName="item_0" /> ... <% if (request.isUserInRole("Managers")){ %> <jsp:useBean id="item_4" class="org.apache.struts.tiles.beans.SimpleMenuItem" /> <jsp:setProperty name="item_4" property="link" value="tools.jsp" /> <jsp:setProperty name="item_4" property="value" value="Tools" /> <tiles:add beanName="item_4" /> ... <% } else if (request.isUserInRole("Users")) { %> <jsp:useBean id="item_8" class="org.apache.struts.tiles.beans.SimpleMenuItem" /> <jsp:setProperty name="item_8" property="link" value="services.jsp" /> <jsp:setProperty name="item_8" property="value" value="Services" /> <tiles:add beanName="item_8" /> ... <% } %> </tiles:putList> </tiles:insert> |
使用 Portlet 布局
清单 10 显示了使用菜单布局的 JSP 页面 (menu.jsp)。将 Portlet 在正文中呈现的列数和 <jsp:useBean>
创建的 Bean 列表 (com.jktelecom.PortletInfo) 传递到 Portlet 布局。Bean 包含 Portlet 布局需要呈现 Portlet 的所有信息(Portlet URL、初始模式和支持的模式)。
清单 10. 使用 Portlet 布局的 JSP 页面
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <%@ page import="java.util.*" %> <%@ page import="com.jktelecom.*" %> <tiles:insert page="/portletLayout.jsp" flush="false" > <!-- create portal table of two columns --> <% if (request.isUserInRole("Managers")) { %> <tiles:put name="numColumns" value="3" /> <% } else { %> <tiles:put name="numColumns" value="2" /> <% } %> <!-- pass portlet links --> <tiles:putList name="portlets" > <jsp:useBean id="portlet_0" class="com.jktelecom.PortletInfo" /> <jsp:setProperty name="portlet_0" property="name" value="IBMSamples/HelloWorld" /> <jsp:setProperty name="portlet_0" property="initialMode" value="<%=PortletInfo.VIEW%>" /> <tiles:add beanName="portlet_0" /> <% if (request.isUserInRole("Managers")) { %> <jsp:useBean id="portlet_1" class="com.jktelecom.PortletInfo" /> <jsp:setProperty name="portlet_1" property="name" value="SunSamples/JSPPortlet" /> <jsp:setProperty name="portlet_1" property="initialMode" value="<%=PortletInfo.EDIT%>" /> <tiles:add beanName="portlet_1" /> … <% } else if (request.isUserInRole("Users")) { %> <jsp:useBean id="portlet_6" class="com.jktelecom.PortletInfo" /> <jsp:setProperty name="portlet_6" property="name" value="IBMSamples/WorldClock" /> <jsp:setProperty name="portlet_6" property="initialMode" value="<%=PortletInfo.VIEW%>" /> <tiles:add beanName="portlet_6" /> … <% } %> </tiles:putList> </tiles:insert> |
如清单 8 所示,列数和 Portlet 列表是在角色的基础上设置的。您还可以从调用 JSP 控制访问 Portlet 的权限。可以使用以下几种方式实现这些自定义:
|
配置门户
将包含 Portlet 的多个页面集成到一个门户时,需要进行特殊的操作才能处理 Portlet URL。当用户单击不同的 Portlet 模式时,将刷新所有其他 Portlet 和整个页面,并且重新发送 Portlet 中的表单数据。
init
标记的 portletUrlPrefix
属性设置为适当的聚合器上下文和映射。聚合器上下文是当前应用程序的上下文根。上面 Portlet 布局部分中已介绍,聚合器映射必须引用当前页面。
清单 11. 门户应用程序部署描述符 (web.xml)
… <servlet> <servlet-name>Home</servlet-name> <jsp-file>index.jsp</jsp-file> </servlet> <servlet> <servlet-name>Tools</servlet-name> <jsp-file>tools.jsp</jsp-file> </servlet> … <servlet-mapping> <servlet-name>Home</servlet-name> <url-pattern>/Home/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Tools</servlet-name> <url-pattern>/Tools/*</url-pattern> </servlet-mapping> … |
在清单 11 中,JSP 页面 (index.jsp) 被定义为假想的 Servlet Home
的实现。聚合映射模式 /Home/*
被引用到 Home
Servlet。具有 /Home/*
模式的 URL 将动态解析 JSP 页面 (index.jsp)。图 2 显示了这一映射关系,并演示 web.xml 的映射如何在 Portlet 模式图标链接的 URL 中显示。
例如,在 init
标记的 portletUrlPrefix
属性中,将相对 URL Home
指定到聚合器映射时(jkTelecom
是上下文根),聚合器模式 /Home/
将解析为 index.xml。当用户在页面中单击 Portlet 的任一图标(视图、编辑和帮助)时,Portlet 模式中请求的表单数据会重新发送,并刷新整个页面(包括所有其他 Portlet)。图 2 演示了这一映射关系。
图 2. 映射关系
|
部署门户
将应用程序打包为 WAR 文件,并作为任一 Web 应用程序进行部署。图 3 和 4 显示了示例代码的部署门户。上下文根指定为 jkTelecom
。如果您在 init
标记中使用 PortletUrlPrefix
属性的相对路径(在 Portlet 布局部分中讨论过),则可以将任一字符串用作上下文根。否则,必须指定您在聚合上下文中使用的同一上下文根。
图 3. 用户 sara 以“管理员”角色部署的门户
图 4. 用户 fred 以“用户”角色部署的门户
|
结束语
现在,您已了解了如何使用 Struts Tile 标记和 Portlet 聚合标记将普通门户布局(页面、导航和 Portlet 布局)创建为可重用的模板。您可以使用该布局创建动态 Web 页,也可以将该布局组装为门户。您已了解了如何解决各种开发和部署问题,以使开发工作更加容易。现在,您可以应用这里介绍的信息,使用 WebSphere Application Server Portlet 容器和聚合标记创建自己的门户页面。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者