标题:
将状态模式扩展为多 Portlet 应用程序
[打印本页]
作者:
心恋无悔
时间:
2004-4-3 18:40
标题:
将状态模式扩展为多 Portlet 应用程序
引言 状态模式是一个有名的软件模式,它是由四个人在他们的书 Design Patterns 中首次提出的。其基本思想为把应用程序看作是状态和动作的组合。Tim Hanis、Skyler Thomas 和 Chris Gerken 于2002年10月将这种模式修改为适合 portlet 开发(参阅相关信息)。 本文描述了如何将状态模式扩展为多 portlet 的应用程序,并且提供了一些技术,可以用来有效地设计这些应用程序。本文演示了如何实现多 portlet 应用程序,其中portlet可以以有效且容易理解的方式进行通信。此外,本文还提供了 IBM®WebSphere®Portal V5(以下称为 WebSphere Portal)的实现示例以及需要对状态模式进行的更改。该实现被部署在 IBM WebSphere Studio Application Developer(以下称为 Application Developer)的 WebSphere Portal Test Environment中。 由于 Applying the State Pattern to WebSphere Portal Portlets -- Part 1: Overview 中所述的原因,您可以选择开发 portlet 应用程序,而不使用已建立的预定义编程模型(如JSP portlet、MVC portlet 或 Strut),Application Developer 为其提供了向导。 状态模式完全控制了 portlet 应用程序,并且提供了非常优秀的代码结构,使得它非常易于重用、更新、理解和维护。它还通过使用专用的动作和状态简化了过程流(process flow)。动作负责处理一类用户请求(如单击表单的发送按钮);而状态则提供应用程序的一个视图(如一个特定的窗口)。 要将状态模式增强为使用多个 portlet 的应用程序,您需要利用 WebSphere Portal 消息传递的概念。消息提供了一个框架,可以用来把数据从一个发送方 portlet 提交给一个或多个接收方 portlet。状态模式环境中的消息可以使状态在发送方或接收方 portlet(或两者)中发生改变。 要发送复杂的数据类型,您可以使用实现 org.apache.jetspeed.portlet.PortletMessage 的消息类,而不要使用 org.apache.jetspeed.portlet.DefaultPortletMessage,它只支持字符串。 样本应用程序的先决条件 附带的应用程序的源代码包含两个 portlet。为了使用这些 portlet 代码,您需要具备以下先决条件: Websphere Studio Application Developer Version 5.0.1 Portal Toolkit V5.0.0.0 WebSphere Portal Test Environment(随 Portal Toolkit 一起安装) WebSphere Portal Server V5.0.0.0(随 WebSphere Portal Test Environment 一起安装) 缺省 Portal V5 Cloudscape 数据库,用于保存联系人条目 页底的下载 ZIP 文件提供了样本应用程序的完整实现。 您应该熟悉前两篇关于状态模式的文章所讲的状态模式的概念(参阅相关信息),并且理解如何用此模式来开发一个 portlet 应用程序。 新状态模式实现的组件 在状态模式实现中,不同类型的对象通过状态管理器 portlet 连接在一起。可以把状态管理器 portlet 看作是调用其他所需对象的控制其对象。状态模式实现所包含的对象有: StateManagerPortlet 所有构成您的应用程序的具体 portlet 类的祖先。该 portlet 本身不知道实现逻辑,而是作为请求分配器(request dispatcher),把请求发送给状态、动作和消息。 Action 从此类继承的类必须实现 actionPerformed 和 setState 方法。动作处理来自触发动作的 portlet 的用户交互。Action 类可以通过消息通知其他 portlet 关于事件的情况。通过调用的动作,可以激活发送方 portlet 的状态改变。 Message 从此类继承的类必须实现 messageReceived 和 setState 方法。消息处理发生在消息发送方 portlet 中的用户交互的结果。可以激活接收方 portlet 状态改变。 State 从此类继承的类必须实现 performView 方法。此处驻留的代码一般会出现在 portlet 的各种 do 方法中。 StateURI custom tag 此类为要使用的 JSP 提供一个自定义标签。该 JSP 使 Action 类与页面上的各种动作相关联。 多 portlet 应用程序的开发方法 想象您需要设计一个带有多个 portlet 的 portlet 应用程序。状态模式可以如何帮助您完成这项工作呢?您从哪里开始实现呢? 当您设计您的应用程序时,您的脑海里应该浮现出应用程序的结构。您可以通过use case 定义或类似于故事板(storyboard)的东西来描述它。您至少需要指定您所需的 portlet 和 portlet 的各种视图是什么。 请考虑以下场景,它是在 Applying the State Pattern to WebSphere Portal Portlets -- Part 2: Implementation 所用的场景的基础上设计的。主要的改变在于您使用了两个 portlet。 场景介绍 您需要有一个联系人列表,它允许用户查看、添加、编辑和删除项。从可用性方面考虑,您决定有两个 portlet: ContactListPortlet 显示您的联系人列表。 ContactListDetailPortlet 显示条目的详细情况,并且可用于添加、编辑和删除项。 取一张纸,把它撕成两半,然后并排放在一起,代表您的两个 portlet。考虑您想要展示给用户的视图——它们将是您的 portlet 的状态。对于简单的应用程序,这些状态是: ContactListPortlet ContactListMainState 联系人列表中所有项的主要视图。 ContactListWaitState 当您的联系人列表中的项被第二个 portlet 更新时出现。ContactListPortlet 显示一个 JSP,指出您的数据此刻正被更改。 ContactListDetailPortlet ContactDetailEmptyState 当 ContactListPortlet 中没有选择联系人时显示。 ContactDetailDisplayState 显示从您的联系人列表中选择的项的详细情况。 AddContactState 显示一个表单,用户可以用来把新的联系人添加到联系人列表中。 EditContactState 当用户正在编辑所选联系人的数据并且将其保存到持久性数据库中时出现,在本例中,持久性数据库为 portal 的 Cloudscape 数据库。 将不同的状态添加到 portlet 的各个部分(用圆表示)。您的页面看起来应该类似于图1。 图1. 只带有 portlet 和状态的状态图
一个 portlet 中的不同状态是通过表示用户交互的动作连接的。另一个 portlet 的状态改变是通过消息触发的。举例来说,如果用户单击您的 ContactListPortlet 中的联系人,那么它将触发 ContactListDetailPortlet 的状态改变,并且通过消息将其状态设置为 ContactDetailDisplayState。 ContactList portlet 的下一个状态与前一个状态将是一样的。 在您的图中,您可以用菱形来表示动作,而用矩形来表示消息,如图2所示。 图2. 图解动作和消息
在本例中,每个动作都使一个消息被发送到接收方 portlet 中。该应用程序有以下动作和消息: ShowDetailsAction 用户单击 ContactListPortlet 中的联系人。该 portlet 保持相同的状态。 ShowDetailsMessage 由 ShowDetailsAction 触发,以显示 ContactListDetailPortlet 中选定的联系人。 AddAction 用户在 ContactListPortlet 中按 Add 按钮。触发 ContactListWaitState 的状态改变。 AddMessage 由 AddAction 触发,以显示 ContactListDetailPortlet 中的 AddContactState。 EditAction 用户在查看 ContactDetailDisplayState 中的联系人时按 Edit 按钮。触发 ContactListDetailPortlet 的状态改为 EditContactState。 EditMessage 由 EditAction 触发,以将 ContactListPortlet 设置为 ContactListWaitState。 SaveAction 用户通过按 Save 按钮保存一个已添加或已编辑的联系人。触发状态改变。如果该动作对于两个场景都是相同的,那么我们就有两种可能的状态改变:ontactListDetailPortlet 的状态从 AddContactState 改为 ContactDetailEmptyState 或从 EditContactState 改为 ContactDetailEmptyState。 SaveMessage 由 SaveAction 触发,以重新加载 ContactListPortlet 中的联系人列表。 DeleteAction 用户在 ContactDetailDisplayState 中按 Delete 按钮。触发 ContactListDetailPortlet 的状态改为 ContactDetailEmptyState。 DeleteMessage 由 DeleteAction 触发,以重新加载 ContactListPortlet 中的联系人列表。 图3显示了最后的状态,表示整个应用程序。 图3. 联系人列表应用程序的完整状态图
这个状态图概括了联系人列表应用程序中所用的全部类和对象。现在,您有了一张辅助图。您可以很容易地把代码分给不同的人承担,因为状态模式为您提供了一个稳定的框架和定义明确的接口。您可以以任何方式扩展此状态图,无论是添加功能还是重构应用程序都可以。 下一部分描述简单的实现。本文提供了准备导入 Application Developer 的代码。 准备数据库 首先,准备 Cloudscape 数据库,以存储您的联系人列表中的持久性数据。 打开命令提示符,进入目录
\runtimes\portal_v50\shared\app\cloudscape\bin。 通过调用 setCP.bat 来设置类路径。 通过调用同一目录中的 cview.bat 来打开 cView Cloudscape 工具。 在 c:\temp\db 目录中创建一个名为 contactList 的新数据库。 选择 File -> New -> Database,然后输入 C:\temp\db\contactList 作为名称。 单击 OK 按钮,这样数据库就创建好了。 现在创建一个新的表,命名为 ADDRESS。 在表中创建具有以下特性的列: Name Type Nullable Length Auto increment OID INT NO N/A YES LASTNAME VARCHAR YES 255 FIRSTNAME VARCHAR YES 255 TITLE VARCHAR YES 32 PHONE VARCHAR YES 32 ADDRESS VARCHAR YES 128 ZIP VARCHAR YES 10 CITY VARCHAR YES 32 COUNTRY VARCHAR YES 32 EMAIL VARCHAR YES 128 COMPANY VARCHAR YES 32 该数据库应该如下所示: 图4. 持久性联系人列表数据的数据库
现在,您可以将一些数据输入表中了。 关闭 Cloudscape cView 工具。 在 Application Developer 中配置数据源 现在,您将创建和配置运行该应用程序的服务器。您可以使用 Application Developer 中的 WebSphere Portal Test Environment 来测试您的应用程序。 在 Application Developer 的 Portlet 透视图中转换到 Server Configuration 视图。右键单击 Servers 项,然后选择 New -> Server 和 Server Configuration。 将服务器命名为 MyPortal。对于 Server Type,选择 WebSphere Portal Version 5.0 - Test Environment。 在创建了服务器之后,双击它,服务器配置就会在 Application Developer 中打开。 在 DataSource 选项卡的服务器设置中,添加一个新的 JDBC 提供者。对于数据库类型,选择 Cloudscape;对于 JDBC 提供着类型,选择 Cloudscape JDBC Provider。将该提供者命名为 Cloudscape。 添加一个新的数据源。选择 Cloudscape JDBC Provider 和 Version 5.0 数据源。 取消选定 container managed persistence,然后将数据源命名为 ds1。 图5. 指定数据源
7.在此数据源的资源特性设置中,将值改为 c:/temp/db/contactList。保存服务器配置。 图6. 服务器配置设置
1.准备样本项目 2.接下来,您将用 Application Developer 导入本文下载部分所提供的样本项目。 3.打开 Application Developer Portlet 向导,然后创建一个空的 portlet。 4.将项目命名为 ContactList,保留 EAR 文件的所有缺省设置。使用 J2EE Version 1.3 / WebSphere Portal 5,然后单击 Finish。 5.将 ContactList.zip 文件保存到您的本地硬盘上,然后将内容解压到您的文件系统的相同位置。 6.在文件系统中,将 Web-inf\source 目录中的 com 目录复制到您的工作区项目的 Java Source 目录(一般为
\workspace\ContactList)。 7.将 Web-inf 目录中除源目录以外的内容复制到您的工作区项目的 Web-inf 目录中。 将 jsp 目录复制到您的工作区项目的 Web Content 文件夹中。 8.回到 Application Developer。右键单击 ContactList 项目,选择 Refresh,以获取您在文件系统层所做的全部更改。再次右键双击 ContactList 项目,选择 Rebuild project。 9.现在,您已经在 Application Developer 中准备了您的项目,并且可以在本地 WebSphere Portal Server Test Environment 中进行运行。 右键单击 ContactList 项目,选择 Run on Server。 在对话框中,选择已配置的服务器 MyPortal 作为服务器。在服务器完成启动之后,您可以看到一个类似于图7的窗口。 图7. 运行样本 portal 应用程序
实现说明 在使用跨 portlet 边界和支持任意数量的 portlet 的过程中,涉及的主要技术是消息传递概念的运用。消息传递允许您在同一 portal 页面上驻留的 portlet 之间发送数据和触发事件。 对于接收消息,StateManagerPortlet实现了 MessageListener。类似于通过 actionPerformed 处理的动作,StateManager Portlet 的 messageReceived() 方法从事件中提取消息类,然后将请求发送到该类。message 类有责任也有业务逻辑对 portlet 容器发出的消息触发作出反应。 public void messageReceived(MessageEvent event) throws PortletException { Message message = (Message) event.getMessage(); message.messageReceived(event, getPortletConfig().getContext()); message.setState(event.getRequest()); } 在接收到消息和调用了 messageReceived() 方法之后,消息类的 setState() 方法也就被执行了。因此,消息必须设置接收 portlet 的下一个状态。在调用状态类的 performView() 方法之前所需的任何类型的事件处理,都可以使用 messageReceived() 方法。 因为消息类实现了 org.apache.jetspeed.portlet.PortletMessage 接口,所以自定义消息可以包含任何类型和任何数量的字段,以将对象和数据从一个 portlet 传送到另一个 portlet。在为您的 portlet 应用程序设计动态行为时,这种能力可以给您提供极大的灵活性。 消息类中的 setState() 方法实现只包含两行代码。下面是来自 EditMessage 类中的示例: public void setState(PortletRequest request) { ContactListWaitState state = new ContactListWaitState(); setState(request, state); } 因此,您需要做的是创建目标状态对象和调用消息超类的 setState() 方法。将状态对象存储到 portlet 会话中。当 PortletContainer 触发对 service() 方法的下一次调用时,就可以从该对象中提取状态对象,并由 StateManagerPortlet 执行。 缺省情况下,消息发送只允许在用 portlet 进行事件处理的过程中发生。因此,它发生在动作类的 actionPerformed() 方法的执行期间。作为一个例子,分析 EditAction 类的 actionPerformed() 方法,它触发了上述 EditMessage: public void actionPerformed(PortletRequest request, PortletContext context) throws PortletException { // perform any other action handling here! EditMessage message = new EditMessage(); context.send(PortletConstants.ADDRESS_LIST_PORTLET, message); } 要使此方法的作用与设计的一样,就需要正确地定义目标 portlet 的名称。此处所用的常量 PortletConstants.ADDRESS_LIST_PORTLET引用 portlet.xml 配置文件中定义的目标 portlet 的名称。 用于创建 JSP 页面中的动作 URI 的StateURI 自定义标签,通过响应对象,把特定动作参数添加到所生成的 PortletURI 中。这是一个字符串参数,因为在 WebSphere Portal 5 提供的 Portlet API 中不支持添加整个动作类。 结束语 在本文中,您了解了如何将有名的状态模式扩展为带有多个 portlet 的 portlet 应用程序。我们还讨论了一种的开发方法,可以用来有效地设计您的应用程序,使代码精炼且容易阅读。
作者:
墓志铭
时间:
2004-4-7 18:59
标题:
将状态模式扩展为多 Portlet 应用程序
我想下载~
欢迎光临 黑色海岸线论坛 (http://bbs.thysea.com/)
Powered by Discuz! 7.2