在本文中,作者阐述了如何设计和实现基于 Web 的接口(称为 portlet),以提供使用网格服务的终端用户接口。他解释了开放源代码门户框架 GridSphere,并提供了 GridSphere 模型中的 portlet 代码示例。本文还介绍了基于 OGSA 的网格服务和 GridSphere 的集成,其中包含一个网格 portlet 示例,展示如何编写一个 portlet 来访问示例 OGSA 计数器服务。 简介:GridSphere 门户框架 在上一年,基于 Portlet 的门户已成为门户服务器平台的最令人兴奋的领域之一。Java Specification Request (JSR)168(即 portlet API)的出现将允许门户供应商提供公共编程模型,该模型允许开发人员更快地将新功能插入到门户服务器中,消费者可以轻松地使用这些功能。现在,许多门户供应商支持 portlet,包括 IBM WebSphere、Sun One Portal Server、Oracle 9iAS 和 Jakarta Jetspeed 项目。 GridSphere 门户 提供一个基于 portlet 的高级开放源代码门户。GridSphere 是在欧盟提供基金的 GridLab 项目 下开发的,提供了一个非常复杂的门户,该门户为各种终端用户提供高度定制的环境。portlet 模型向用户提供了一个灵活易用的接口,并向门户开发人员提供了一个模型,用于创建可插入的和动态的应用程序支持。下面简要列出核心的 GridSphere 特性: - Portlet API 的实现与 IBM WebSphere Portal Server 4.2 或更高版本紧密兼容。
- 易于开发并易于与插入到 GridSphere portlet 容器中的“第三方 portlet”集成。
- 级别更高的模型,用于使用可视的 bean 和 GridSphere User Interface (UI) 标签库构建复杂的 portlet。
- 灵活的基于 XML 的门户表示描述,可以轻松对其进行修改来创建自定义的门户布局。
- 对基于角色的访问控制(Role Based Access Control,RBAC)提供内置的支持,RBAC 将用户分为 guest、user、admin 和 super user。
- 复杂的服务模型,允许创建“用户服务”,可以按照用户权限限制服务方法。
- 通过 Hibernate for RDBMS 数据库支持提供数据的持久性。
- 为 portlet 服务的服务端测试集成了 Junit/Cactus 单位测试,包括生成测试报告。
- GridSphere 核心 portlet 提供基本功能,包括登录、注销、用户和访问控制管理。
由于 GridSphere portlet API 几乎与 WebSphere portlet API 完全相同,所以您应该多看看 developerWorks 文章,这些文章在 参考资料 和 IBM WebSphere Portal Zone 中列出。在理论上,使用 IBM WebSphere Portal Server 开发本文中的示例 portlet 应该不难。GridSphere API 与 JSR Portlet API 1.0 最后的规范非常类似,gridSphere 的将来版本是符合 JSR 168 的。 当前的 GridSphere 版本提供了一个门户、一个 portlet 容器和一组有用的核心 portlet,包括用户和组管理以及布局定制和 portlet 订阅。 安装 GridSphere 从 http://www.gridsphere.org/ 下载最新的 GridSphere 版本并按照指示安装该门户。前提条件是您必须在系统中安装了 Java 技术 Ant 和 Tomcat 4.1.X servlet 容器。安装了 GridSphere 后,可以只启动 Tomcat,然后在 Web 浏览器中,单击 http://127.0.0.1:8080/gridsphere/gridsphere 查看该门户。初始配置允许您以“root”身份登录,而且不用输入口令。登录后,转至 Administration->Users 选项卡,单击 root 用户来编辑配置文件。更改口令和其他设置,包括您的登录名和电子邮件地址。登录后,您就成为 GridSphere 门户管理员。 图 1. GridSphere 用户管理器 portlet
网格服务简介 在提供对用户的虚拟组织(Virtual Organization,VO)内的计算资源和数据资源的安全统一访问方面,门户正变得越来越复杂,所以网格 portlet 必须依靠某种类型的中间件来执行这些操作。开放网格服务基础设施(Open Grid Services Infrastructure,OGSI)规定如何创建网格服务,而 OGSI 是整体开放网格服务体系结构(Open Grid Services Architecture,OGSA)的一部分。Globus Toolkit 3.0 (GT3) 提供了 OGSI 的 Java 参考实现。GT3 提供了强制性网格服务特性,如服务调用、生存期管理、服务数据接口、依赖底层公钥基础设施(Public Key Infrastructure,PKI)的安全接口。网格服务扩展了 Web 服务,因此在实际中,编程模型很相似。 简而言之,使用 GT3 创建网格服务的步骤如下所示: - 创建定义服务接口的 WSDL 接口。可以采用工具转换采用 Java 或 C 语言编写现有接口以生成 WSDL。
- 使用 GT3 工具从 WSDL 服务器接口生成客户机和服务器存根(stub)。
- 填写服务器骨架(skeleton)实现代码。
- 配置服务部署描述符并部署服务到宿主容器。
幸运的是,GT3 与一组供我们使用的示例服务绑定在一起。因此,我们将集中讨论使用现有服务和现有的客户机存根代码来访问服务。 幸运的是,您下载的 Tomcat 服务器还可以充当 OGSI 网格服务宿主环境,允许 GridSphere 门户框架和 OGSA GT3 示例服务一起运行。 下一节准备开发示例 portlet,您应从 Globus Web site 下载最新的 GT3 版本。要构建示例并部署到 Tomcat 容器中,请发出下列命令,其中 $CATALINA_HOME 设置为下载 Tomcat 的位置。 %> ant %> ant samples %> ant -Dtomcat.dir=$CATALINA_HOME deployTomcat | 现在,由于示例 portlet 需要一些 GT3 类,所以需要将 OGSA Web 应用程序的库复制到 portlet 可以找到的 Tomcat 共享库目录。 %> cp $CATALINA_HOME/webapps/ogsa/WEB-INF/lib/*.jar $CATALINA_HOME/shared/lib/ | 根据 GT3 User‘s Guide(请参阅“参考资料”),最后一步是将下列条目添加到 $CATALINA_HOME/conf/web.xml 中,配置 Tomcat 以接受 WSDL、GWSDL 和 XSD 文件扩展名: <mime-mapping> <extension>gwsdl</extension> <mime-type>text/xml</mime-type> </mime-mapping> <mime-mapping> <extension>wsdl</extension> <mime-type>text/xml</mime-type> </mime-mapping> <mime-mapping> <extension>xsd</extension> <mime-type>text/xml</mime-type> </mime-mapping> | 编写网格计数器服务 portlet 在网格 portlet 示例中,我们使用 Globus Toolkit 提供的示例 OGSI 计数器服务编写一个简单的加减数字的 portlet。计数器服务表示为 CounterPortType,它是基 GridService 接口的子类,有三个方法,如下面的图 2 所示: 图 2. 计数器服务的 UML 图
add(int a) 和 subtract(int b) 方法添加或减去提供的值并返回新值。getValue() 方法只返回静态计数器值的当前结果。 1. 编写 portlet 在计数器 portlet 示例中,用户可以输入整数值,然后单击 "Add" 或 "Subtract" 按钮加上或减去计数器服务的值,并显示已更新的计数器值。 当第一次实例化 portlet 时,init() 方法被调用,向该 portlet 提供由任何后续 portlet 请求使用的所有必需的初始信息或配置信息。portlet 编程模型将表示(portlet 的呈现)和逻辑(当某个动作发生时必须执行的操作)分为不同的 portlet 方法。portlet 容器负责调用 doView() 呈现方法来显示 portlet,并在 portlet 收到事件(如按钮单击或表单提交)时,负责调用 actionPerformed() 方法。Portlet 可以向用户提供几种模式,包括 Edit、Configure 或 Help,从而向用户提供特定模式的接口。portlet 必须提供适当的呈现方法,如 doEdit 用于支持编辑模式。对于熟悉 servlet 的人员而言,portlet 开发非常相似,只是 servlet 的 doGet 或 doPost() 方法由 actionPerformed() 和 doXXX() portlet 方法取代。portlet 编程和 servlet 编程的另一个显著差别在于 PortletResponse、 PortletConfig 和 PortletContext 类的使用,这些类在现有的由 Java Servlet API 提供的 HttpServletRequest 、HttpServletResponse 、ServletConfig 和 ServletContext 类的基础上装饰(也就是提供)了附加的功能。 最终的 portlet 如下所示: 清单 1. 计数器服务 portlet public class GridCounterPortlet extends AbstractPortlet { public static final String VIEW_JSP = "/jsp/ogsa/counter/view_counter.jsp"; // Create a URL for the counter service private static final String counterUrl = "http://127.0.0.1:8080/ogsa/services/samples/counter/basic/CounterFactoryService"; private LocatorType locator = null; private CounterPortType counter = null; public void init(PortletConfig config) throws UnavailableException { super.init(config); try { // Create a grid service handle from the service URL URL GSH = new URL(counterUrl); // Get a reference to the CounterService Factory OGSIServiceGridLocator gridLocator = new OGSIServiceGridLocator(); Factory factory = gridLocator.getFactoryPort(GSH); GridServiceFactory counterFactory = new GridServiceFactory(factory); // Create a new CounterService instance and get a reference to its Counter PortType. locator = counterFactory.createService(); CounterServiceGridLocator counterLocator = new CounterServiceGridLocator(); counter = counterLocator.getCounterPort(locator); } catch (Exception e) { getPortletLog().error("Unable to create OGSA counter service instance", e); } } public void actionPerformed(ActionEvent evt) throws PortletException { PortletRequest req = evt.getPortletRequest(); int count = 0; String countStr = req.getParameter("count"); try { count = Integer.parseInt(countStr); } catch (IllegalArgumentException e) { // do nothing } // get the operation to perform from the request String operation = req.getParameter("operation"); if (operation != null) { if (operation.equals("Add")) { try { System.err.println("adding " + count + " to counter"); counter.add(count); } catch (RemoteException e) { getPortletLog().error("Failed invoking add on counter", e); } } if (operation.equals("Subtract")) { try { counter.subtract(count); } catch (RemoteException e) { getPortletLog().error("Failed invoking subtract on counter", e); } } } } public void doView(PortletRequest req, PortletResponse res) throws IOException, PortletException { // a portlet uri creates a link for this form to refer back to the calling portlet PortletURI uri = res.createURI(); uri.addAction("submit"); req.setAttribute("uri", uri.toString()); String counterVal = "0"; try { // obtain the current counter value from the grid service counterVal = String.valueOf(counter.getValue()); } catch (RemoteException e) { getPortletLog().error("Unable to get counter value", e); } // put the counter value in the request to display in the JSP req.setAttribute("counterVal", counterVal); getConfig().getContext().include(VIEW_JSP, req, res); } public void doHelp(PortletRequest req, PortletResponse res) throws IOException, PortletException { PrintWriter out = res.getWriter(); out.println("This portlet demonstrates the GT3 counter service."); } } | 如清单中所示,init() 方法负责通过代表服务端点的硬编码 counterUrl 获得计数器服务(一种 portType)的实例。在此情况下,portlet 使用同一个容器中部署的计数器服务,但也可以容易地修改它,以便使用其他一些已部署的服务。portlet 容器只调用 init() 方法一次,产生由所有传入客户机共享的单个计数器服务实例。actionPerformed() 方法使用通过表单提交的请求参数,调用具有提供值的计数器服务的加减方法。doView() 方法查询计数器服务获得该值,并通过包含如下所示的 JSP 页面来显示表单: 清单 2. 计数器服务 JSP 表单 <jsp:useBean id="counterVal" class="java.lang.String" scope="request"/> <jsp:useBean id="uri" class="java.lang.String" scope="request"/> <form action="<%= uri %>" method="POST"> <table cellspacing="1" cellpadding="1" border="1"> <tr> <td>Enter Number: </td> <td><input type="text" name="count"/></td> </tr> <tr> <td>Counter Value: </td> <td align="center"><b><%= counterVal %></b></td> </tr> <tr> <td align="center"><input type="submit" name="operation" value="Add"/></td> <td align="center"><input type="submit" name="operation" value="Subtract"/></td> </tr> </table> </form> | 以下 portlet 中给出的表示 JSP 提供了一个简单的表单,该表单使用一个文本域和两个提交按钮来从计数器中加或减一个新值。JSP bean counterval 和 uri 用于动态显示计数器值,创建一个表单动作,以便向后引用正在调用的 portlet。尽管该页面展示的是最简单的 HTML 表单,但 GridSphere 提供了非常有用的标签库,用于构造用户界面并减少公共例程(如 PortletURI 的创建和存储),将其放入特殊的 <ui:actionlink> 或 <ui:form> 标签来创建超链接和表单,以便向后引用正在调用的 portlet。JSR 168 Portlet 1.0 规范还支持额外的标签,而且该规范将在 GridSphere 中得到支持。将来还要撰写文章详细讨论 GridSphere 标签库的用法和优势。 2. 创建门户部署描述符 将新 portlet 部署到 GridSphere 的最后一步是创建 GridSphere 使用的部署描述符。portlet 部署描述符 portlet.xml 如下所示 : 清单 3. portlet 部署描述符 <?xml version="1.0" encoding="UTF-8"?> <portlet-app-collection> <portlet-app-def> <portlet-app id="org.gridlab.gridsphere.portlets.GridCounterPortlet"> <portlet-name>Grid Counter Portlet</portlet-name> <servlet-name>GridCounter</servlet-name> <allows> <maximized/> <minimized/> <resizing/> </allows> <supports> <view/> <help/> </supports> </portlet-app> <concrete-portlet-app id="org.gridlab.gridsphere.portlets.GridCounterPortlet.1"> <concrete-portlet> <portlet-name>Grid Counter Portlet</portlet-name> <default-locale>en</default-locale> <language locale="en"> <title>Grid Counter Portlet</title> <title-short>Grid Counter Portlet</title-short> <description>Demonstration of OGSI counter service</description> <keywords>ogsa ogsi counter grid</keywords> </language> </concrete-portlet> </concrete-portlet-app> </portlet-app-def> </portlet-app-collection> | portlet 描述符提供 GridSphere 容器使用的 portlet 属性的定义。portlet 必须定义一个应用程序 portlet 定义和一个或多个 concrete portlet 定义,后者代表由门户使用的实际 portlet 实例。在该定义中,我们规定 portlet 支持 VIEW 和 HELP 模式。它还支持所有的窗口状态,因此用户可以最小化、最大化或调整 portlet 的大小,就像它是用户桌面上的一个正常的应用程序窗口。concrete portlet 定义提供了一个由该 portlet 显示的标题,以及可能由容器使用的描述和关键字。 在 GridSphere 中,portlet 与 servlet 类似,还必须在 web.xml web 应用程序部署描述符中注册,该部署描述符由 Servlet API 定义,通过 servlet 容器进行注册。该 web.xml 如下所示: 清单 4. Web 应用程序部署描述符 <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2.3.dtd"> <web-app> <display-name>GridSphere Grid Portlets</display-name> <description> Provides an OGSA counter service portlet </description> <servlet> <servlet-name>GridCounter</servlet-name> <servlet-class>org.gridlab.gridsphere.portlets.GridCounterPortlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>GridCounter</servlet-name> <url-pattern>/gridcounter</url-pattern> </servlet-mapping> </web-app> | 在 web.xml 中指定的 servlet 类必须与实际的 portlet 类名称相匹配,而 servlet-name 必须与 portlet.xml 描述符文件中应用程序 portlet 定义值中指定的名称相匹配。这样就允许 GridSphere 容器访问 portlet 来转发请求。 最后,门户需要一个布局描述符,它提供默认布局,指出 portlet 在门户中的布局。GridSphere 门户依靠选项卡式窗格方法组织 portlet,以便显示给用户。portlet web 应用程序包含布局描述符 layout.xml,它根据 GridSphere 布局引擎使用的可视化组件来描述其布局。我们的 layout.xml 示例如下所示: 清单 5. 门户布局描述符 <?xml version="1.0" encoding="UTF-8"?> <portlet-tabbed-pane> <portlet-tab title="Grid samples"> <portlet-tabbed-pane style="sub-menu"> <portlet-tab> <title lang="en">OGSI examples</title> <table-layout> <row-layout> <column-layout width="50%"> <portlet-frame label="counter"> <portlet-class>org.gridlab.gridsphere.portlets.GridCounterPortlet.1</portlet-class> </portlet-frame> </column-layout> </row-layout> </table-layout> </portlet-tab> </portlet-tabbed-pane> </portlet-tab> </portlet-tabbed-pane> | 在 GridSphere 布局中,双选项卡式窗格提供了一种嵌套的组织结构,用于显示在布局描述符中指定的 portlet(和其他组件)。由于我们只有一个 portlet,所以,我们选择将其放置在它自己的选项卡中,默认宽度占到屏幕的一半。<portlet-frame> 表示 portlet 的可视化显示,包括受支持的模式和窗口状态的标题栏。必须由 <portlet-frame> 包含的 <portlet-class> 元素指定了concrete portlet 标识符,该标识符在 portlet 部署描述符中指定。 4. 试一试! 您现在准备在 GridSphere 门户中尝试执行计数器 portlet 示例。请在“参考资料”部分下载 counterportlet.war 文件,该文件包含了以前的文件和两个附加库,用于存放 JSP 中使用的标签库。只需将该文件放在 Tomcat 的 webapps 目录(如 $CATALINA_HOME/webapps )中,然后编辑类似于以下的 $CATALINA_HOME/webapps/gridsphere/WEB-INF/PortletMaster.xml 文件: 清单 6. GridSphere portlet 主配置 <portlet-services> <service> <name>Portlet Manager Service</name> <user-required>true</user-required> <description>Provides Administration Capabilities for Portlet Web Applications</description> <interface>org.gridlab.gridsphere.services.core.registry.PortletManagerService</interface> <implementation>org.gridlab.gridsphere.services.core.registry.impl.PortletManagerServiceImpl</implementation> <service-config> <param-name>startup-portlet-webapps</param-name> <param-value>gridsphere, counterportlet</param-value> </service-config> </service> </portlet-services> | 需要编辑的唯一一行是 param-value 元素,它指定哪个 portlet web 应用程序应被 GridSphere 容器所知晓。通过将 counterportlet 添加到逗号分隔的列表,GridSphere 将在它启动时加载和初始化网格计数器 portlet。 现在,在您重新启动 Tomcat servlet 容器时,登录并导航到 Welcome->Settings,在此,您可以从 "Profile Manager" portlet 的编辑模式选择要加入的新 "counterportlet" 组。 图 3. 计数器服务 portlet
结束语 本文简要介绍了实现网格 portlet 所用的技术,并演示了一个网格计数器服务 portlet 的示例,该示例使用 Globus toolkit 和 GridSphere 门户框架一起来展示示例计数器服务。GridSphere 门户和 GT3 OGSI 服务形成自然的共存关系,您可以容易地开发 portlet 并利用 OGSI 服务的优点。该计数器服务 portlet 可能是最容易的并且能够工作的 portlet 示例,展示了使用 GT3 的网格服务。在将来的文章中,我们将讨论: - 如何封装多个 portlet 使用的公共逻辑。
- 如何创建包括更复杂操作和用户界面的 portlet。
- 如何开发管理 portlet 来配置 OGSI 网格服务。
- 如何使用网格安全机制支持安全网格服务。
- 如何提供内容的本地化。
致谢 我要感谢所有那些使用和促进 GridSphere 的人,感谢他们的帮助和讨论,这有助于使 GridSphere 框架更具可用性,包括我的同事 Michael Russell 和 Oliver Wehrens 以及 GridLab 项目中的其他人员。还要感谢 Globus 项目的 Sam Meder,他提供了集成 GT3 和 GridSphere 方面的建议和忠告。本工作得到了 European Commission 5th Framework 计划 (grant IST-2001-32133) 的帮助和支持,该计划是 GridLab 项目的主要资金来源。 参考资料 - 了解有关 GridSphere 门户框架的更多信息。
- 下载本文中使用的计数器 portlet Web 应用程序(包括源代码)。
- 阅读 Globus Toolkit 3 User‘s Guide。
- 另外,请参阅 Globus Toolkit 3 Programmer‘s Tutorial。
- 请参阅文章 The case for portlets,本文介绍了如何决定 portlet 是您的最佳选择(developerWorks,2003 年 2 月)。
- 文章 Create your own portlet and Web service 介绍了 portlet 和 Web Services 编程(developerWorks,2002 年 1 月)。
- 请参阅文章 Developing Grid Computing Applications,本文向开发人员介绍网格计算的基本概念和开放网格服务体系结构 (OGSA) (developerWorks,2002 年 10 月)。
- 开发人员对于 OGSI 和基于 OGSI 的网格计算的概述 介绍了开放网格服务体系结构 (OGSI) 规范,包括命名和引用网格服务,使用所有网格服务公共的接口和行为(developerWorks,2003 年 4 月)。
- 请参阅文章 Globus Toolkit 3.0 和 OGSI 体系结构 — 概述 ,本文介绍了 Globus GT3 软件体系结构、编程模型和 GT3 引入的工具、Web 服务和网格服务软件模型之间的关系,以及处理引入到 Web 服务领域的新网格服务行为(developerWorks,2003 年 5 月)。
- 请参阅文章 用 Globus Toolkit 3.0 编写安全的网格服务,本文介绍 Globus Toolkit 3.0 的安全技术和机制(developerWorks,2003 年 10 月)。
|