内容


WebSphere Portal 编程

普及的 Portlet 开发

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: WebSphere Portal 编程

敬请期待该系列的后续内容。

此内容是该系列的一部分:WebSphere Portal 编程

敬请期待该系列的后续内容。

© Copyright International Business Machines Corporation 2002. All rights reserved.

介绍普及的门户

目前在因特网用户和 Web 内容开发者中,门户非常流行。门户已经变成了最终用户和企业用户都过来使用应用程序、访问信息的地方。通过定制,门户为用户提供了一些灵活性,使他们可以定制自己的视图和布局,甚至把内容个性化为他们最感兴趣的主题(比如股票报价、天气或旅游)。

企业对门户尤其狂热,因为门户为企业的重要信息提供了一个可管理的、稳定的界面。在内部,公司可以规定员工可以使用哪些内容和应用程序(被称为 portlet),并可以根据员工的职位定制这些内容。其他用户可以利用易于使用的界面来访问公司提供给他们的信息(通常是极大量的)。

传统上是通过 Web 浏览器界面,如 Microsoft®Internet Explorer(IE)或 Netscape Navigator® 查看门户,这些浏览器界面是胖客户机,允许用户同时查看多个应用程序,并考虑到了使用 Java Applet 或富媒体(rich media),比如 Macromedia Flash™ 进行客户机端处理,因此 Web 开发者有大量的各种各样的工具,使用这些工具可以通过门户界面把内容呈现在用户面前。

图 1. 浏览器上的 IBM WebSphere Portal
浏览器上的 IBM WebSphere Portal
浏览器上的 IBM WebSphere Portal

随着时间的推移,企业已经逐渐使它们的员工以更易移动的方式工作,对于那些员工必须出差到客户所在地的行业的企业来说(比如快递或设备维修服务行业)更是如此。此外,消费者接受移动个人设备的热情依然不减。这两种需求结合起来已经促进了对移动个人数据助理(Personal Data Assistant,PDA)设备(如手持设备(Palm™handheld)和袖珍 PC 机(PocketPC)及其它更受限制的设备,如智能电话(SmartPhone))的需求。随着这些设备变得日益普及,从这些设备访问台式机类应用程序的需求持续增长,门户将成为用来把应用程序和信息呈现给那些需要它们的移动用户的一种重要方法。

图 2. 智能电话上的 IBM WebSphere Portal
智能电话上的 IBM WebSphere Portal

本文将介绍 Web 开发者到一个普及的环境进行 portlet 编程,这意味着我们将讨论为设备,而不是为传统的浏览器(如 IE 或 Netscape®),创建 portlet 应用程序,本文针对的设备包括手持设备、袖珍 PC 机和智能电话。本文旨在使开发者至少具有一定的 IBM®WebSphere® Portal portlet 开发经验,并以 portlet 设计的基本概念为基础,使得为普及型设备开发应用程序更加容易。

Portlet 编程回顾

让我们快速看一下一个显示静态内容的简单 portlet(图 3),重新温习一下 portlet API。如果您完全不了解 portlet API,或者不了解如何在 portal 服务器中部署 portlet,请参考 WebSphere Portal InfoCenter(请参阅 参考资料)中的文档,这些文档对 API、每个标记(tag)以及相关的标记值都进行了完整的描述。

图 3. 简单的静态 Weather Portlet 代码样本

package com.ibm.wps.samples.weather;
import java.io.*;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
/**
 * WeatherPortlet.java ™Simple Example to display weather.
 */
public class WeatherPortlet extends AbstractPortlet {
  public void init(PortletConfig portletConfig) throws UnavailableException {
      super.init(portletConfig);
  }
  public void service(PortletRequest request, PortletResponse response)
    throws PortletException, IOException {
      PrintWriter writer = response.getWriter();
      writer.println("<p>The weather is 78 degress.</p>");
  }
}

这是一个非常基本的 portlet,它只是输出静态消息“The weather is 78 degrees”。它显示了基本的类接口 AbstractPortlet,要创建新的 portlet 应用程序需要继承这个接口。与 servlet 一样,portlet 有一个 service 方法,为向 portlet 窗口返回内容,必须覆盖这个方法。这个示例显示了如何从 PortletResponse 对象获取 PrintWriter 以便将 HTML 内容写到输出流。

图 4. 简单的静态 Weather Portlet 输出
简单的静态 Weather Portlet 输出
简单的静态 Weather Portlet 输出

我们的下一个示例更复杂些。在图 5 中,portlet 从天气服务中动态检索指定的邮政编码所代表的地区的天气情况。此外,消息也不再是硬编码的,而是委托给 JSP 去处理。

图 5. 增强的 Weather Portlet 代码样本

package com.ibm.wps.samples.weather;
import java.io.*;
import vendor.service.weather.*;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
import org.apache.jetspeed.portlet.service.*;
/**
 * WeatherPortlet.java ™A more extensive example to display weather.
 */
public class WeatherPortlet extends PortletAdapter {
  protected static final String jsp =
    "/WEB-INF/weather/html/DisplayWeather.jsp";
  public void init(PortletConfig portletConfig) throws UnavailableException {
      super.init(portletConfig);
  }
  public void doView(PortletRequest request, PortletResponse response)
    throws PortletException, IOException {
      // Create a Weather Bean.
      WeatherBean weatherBean = new WeatherBean();               
         
      // Get the portlet session.
      PortletSession session = request.getPortletSession();
      // Get the portlet configuration.
      PortletConfig config = getPortletConfig();
      // Get the portlet context.
      PortletContext context = config.getContext(); 
      // Get portal user object.
      User user = session.getUser(); 
      // Get the user's full name or null if not available.
      weatherBean.setFullName(user.getFullName()); 
      // Get the weather service for determining the temperature.
      WeatherService weatherService = 
        (WeatherService)context.getService(WeatherService.class);
      // Get the current temperature for the specified zip code.
      weatherBean.setTemperature(
        weatherService.getTemperature(
          Integer.parseInt(config.getInitParameter("ZipCode"))));
      // Put the bean in the request.
      request.setAttribute("weatherBean", weatherBean); 
      // Invoke the JSP to do the actual rendering for the portlet view.
      context.include(jsp, request, response);
  }
}

我们来仔细研究一下这个示例来考察其中的一些重要组件。第一件要注意的事情是 WeatherPortlet 类不再继承 AbstractPortlet 类,而是继承 PortletAdapter 类。PortletAdapter 类是中心 portlet 抽象,即 portlet 接口的一个实现。它为下列 portlet 方法提供缺省的空实现:init、login、service、logout 和 destroy。

接下来要注意的是该类不再直接覆盖 service 方法。而是覆盖与特定的 portlet 模式相关联的方法。Portlet 有四种模式的操作:

  • Portlet.Mode.VIEW
  • Portlet.Mode.EDIT
  • Portlet.Mode.HELP
  • Portlet.Mode.CONFIGURE

从这些模式的名称中就可以看出它们的操作。对于每种模式,PortletAdapter 类都提供一个缺省的空实现方法。在图 5 中,portlet 只支持 VIEW 模式,所以只有一个相应的 doView 方法被覆盖。service 方法的缺省实现确定 portlet 模式并调用适当的模式方法,下图 6 中的代码片段指出了这一点:

图 6. 确定 portlet 模式

...
public void service(PortletRequest request, PortletResponse response)
  throws PortletException, IOException {
    // Get the mode of the portlet.
    Portlet.Mode mode = request.getMode();
    if (mode == Portlet.Mode.VIEW)
      doView(request, response);
    else if (mode == Portlet.Mode.EDIT)
      doEdit(request, response);
    else if (mode == Portlet.Mode.HELP)
      doHelp(request, response);
    else if (mode == Portlet.Mode.CONFIGURE)
      doConfigure(request, response);
}
...

我们来研究一下当调用 portlet 去处理它的 VIEW 模式时会发生什么情况。

发生的第一件事是将创建一个 WeatherBean 实例(图 7),这个实例将包含处理显示所需的所有相关数据,在本例中是用户名和预先配置的邮政编码所代表的地区的天气情况。如代码所示,用户名是从 user 对象检索出来的。为确定温度,首先从 PortletConfig 对象检索邮政编码(这里,我们简化的假设是这个 portlet 是由管理员为某个邮政编码所代表地区的全部用户配置的)。第二件事是获取对 PortletService 的引用(具体到这个示例就是引用 WeatherService 对象)。为限制这个示例的复杂性,我们将假设已经向门户服务器注册了天气服务,并且 portlet 可以利用这个服务获取特定的邮政编码所代表的地区的当前天气情况。一旦获得了这两条信息,就将它们存储在 WeatherBean 中。

图 7. WeatherBean 代码样本

package com.ibm.wps.samples.weather;
/**
 * WeatherBean.java ?A bean used to pass data to the JSP display.
 */
public class WeatherBean {
  /** The user's full name. */
  private String fullName = "";
  /** The current temperature. */
  private int degrees = 0;
  public void setFullName(String name) {
    fullName = name;
  }
  public String getFullName() {
    return fullName;
  }
  public void setTemperature(int degrees) {
    this.degrees = degrees;
  }
  public int getTemperature() {
    return degrees;
  }
}

这个 bean 被从 PortletAdapter 类传递到 JSP,这个 JSP 将最终处理显示。这是通过 PortletRequest 对象的 setAttribute 方法完成的。然后 JSP 将使用 useBean 标记建立对 WeatherBean 的引用,并将用户的全名值和当前的温度嵌入到 HTML 输出标志(markup)中。

图 8. DisplayWeather 代码样本

<!----------------------------------------------------------------------------->
<!--                                                                         -->
<!-- DisplayWeather.jsp - A JSP used to render the data for the weather      -->
<!-- portlet.                                                                -->
<!--                                                                         -->
<!----------------------------------------------------------------------------->
<%@ page contentType="text/html" errorPage="" %>
<jsp:useBean id="weatherBean"
     class="com.ibm.wps.samples.weather.WeatherBean"
     scope="request"/>
<p>
Hi <%= weatherBean.getFullName() == null ? "" : weatherBean.getFullName() %>!
<br>
The current temperature in your area is <%= weatherBean.getTemperature() %>.

这个 portlet 的输出(图 9)看起来与我们前面的示例相似。

图 9. 增强的 Weather Portlet
增强的 Weather Portlet
增强的 Weather Portlet

目前为止,我们只研究了严格按浏览器(如 IE 和 Netscape,它们实现整个 HTML 规范)处理其显示的 portlet。但如果您想将 portlet 扩展到这些胖客户机之外,并使内容可以由受限的设备(比如袖珍 PC 机、手持设备或智能电话)使用时,又会怎样?在下面的几部分中,我们将研究 IBM WebSphere Portal 提供的额外 API,这些 API 可以帮助为这些设备进行开发工作。我们还将研究更高级的问题,这些问题目前在这些 API 中尚未解决,我们甚至还将演示增加发布的方法的方法。

这个示例中并没有讨论关于一些更高级 portlet API 论题的细节问题,比如窗口状态、事件处理、实例数据等,相关信息请参考 WebSphere Portal InfoCenter(请参阅 参考资料)。

介绍普及的编程

面向对象设计的其中一个设计模式是模型-视图-控制器(Model-View-Controller),也称 MVC。MVC 简单的令人惊讶,但却是一个非常有用的模式,它要求开发者按照下面三个基本组件来构思应用程序:

  • 模型(model)是主要的组件,它维护应用程序呈现出的状态和数据模型。
  • 控制器(controller)是模型及其视图之间的桥梁。它是一个组件,用户可以使用它来更改底层数据模型。
  • 视图(view)是数据模型面向用户的图形化表示。

这种设计的强大之处在于它提供了:

  • 通明性?这种设计的逻辑组件是明确分开的。
  • 模块性?应用程序的各种组件可以按您的需要调进调出,使调试工作更加容易。
  • 多个视图?在多个实现中视图是可伸缩的,可以用来体现同一个数据模型。
  • 可伸缩的设计?随着应用程序的增长,各种组件也能够增长,同时不影响底层的应用程序逻辑。
图 10. 模型-视图-控制器(MVC)设计模式
模型-视图-控制器(MVC)设计模式
模型-视图-控制器(MVC)设计模式

普及的 portlet 编程技术

既然我们了解了进行应用程序开发的模块化方法的背景知识,那么我们就把它应用到普及的编程中吧。我们可以对天气(Weather)示例中的组件做如下分析:

模型:核心天气应用程序,比如通过访问用户的全名或天气服务来检索当前的温度。
控制器:供应 bean,这些 bean 把 portlet 代码和 JSP 之间的数据连接起来。
视图:处理 portlet 内容的图形化显示的 DisplayWeather JSP。

现在很容易就可以看到不管处理的实际显示是什么样子,模型都维持不变。在各个视图之间控制器基本上是保持不变的,但可能需要按照期望的视图对数据执行一些特定的定制工作。视图组件具有处理数据使它们最适合目标显示器的灵活性。例如,智能电话以 WML(无线标记语言(Wireless Markup Language))格式显示内容。于是,对于我们的天气示例,我们可以为每个期望的设备定义一个单独的视图 JSP:为浏览器定义一个 HTML JSP,为智能电话定义一个 WML JSP。看一下图 11 中的样本 portlet,它使用这个设计模式和 WebSphere Portal 的普及 API。

图 11. 为每个设备定义单独的视图

package com.ibm.wps.samples.weather;
import java.io.*;
import com.ibm.wps.portlets.*;
import vendor.service.weather.*;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
import org.apache.jetspeed.portlet.service.*;
/**
 * WeatherBaseController.java
 * Base controller class containing common controller functionality.
 */
public class WeatherBaseController extends AbstractPortletController {
  /** The View for HTML devices (e.g. Internet Explorer, Netscape). */
  protected String jspHTML = "/WEB-INF/weather/html/";
  
  /** The View for WML devices (e.g. SmartPhones). */
  protected String jspWML = "/WEB-INF/weather/wml/";
 
  public void init(PortletConfig config)
    throws UnavailableException {
      super.init(config);
      // Load the HTML View from the configuration
      jspHTML += config.getInitParameter("view.HTML");
 
      // Load the WML View from the configuration.
      jspWML += config.getInitParameter("view.WML");
  }
  protected void createBean(PortletRequest request, PortletResponse response) {
      // Get the portlet configuration object.
      PortletConfig config = getPortletConfig();
      // Create a Weather Bean.
      WeatherBean weatherBean = new WeatherBean();
      // Set the user's full name or null if not available.
      weatherBean.setFullName(
        request.getPortletSession().getUser().getFullName());
      // Get the weather service for determining the temperature.
      WeatherService weatherService = 
        (WeatherService)config.getContext().getService(WeatherService.class);
      // Get the current temperature for the specified zip code.
      weatherBean.setTemperature(
        weatherService.getTemperature(
          Integer.parseInt(config.getInitParameter("ZipCode"))));
      // Insert bean into the request for the JSP to use.
      request.setAttribute("weatherBean", weatherBean);
  }  
}
package com.ibm.wps.samples.weather;
import java.io.*;
import com.ibm.wps.portlets.*;
import org.apache.jetspeed.portlet.*;
/**
 * WeatherHTMLController.java
 * Controller for calling the HTML View.
 */
public class WeatherHTMLController extends WeatherBaseController {
  public void doView(PortletRequest request, PortletResponse response)
    throws PortletException, IOException {
      createBean(request, response);
      getPortletConfig().getContext().include(jspHTML, request, response);
  }
}
package com.ibm.wps.samples.weather;
import java.io.*;
import com.ibm.wps.portlets.*;
import org.apache.jetspeed.portlet.*;
/**
 * WeatherWMLController.java
 * Controller for calling the WML View.
 */
public class WeatherWMLController extends WeatherBaseController {
  public void doView(PortletRequest request, PortletResponse response)
    throws PortletException, IOException {
      createBean(request, response);
      getPortletConfig().getContext().include(jspWML, request, response);
  }
}

这个示例显示了一个基本的控制器,它包含 HTML 和 WML 视图的公共功能。首先,在 init 方法中,加载来自 portlet 配置的 HTML 和 WML 视图 JSP 的名称。然后,提供一个名为 createBean 的方法,这个方法创建一个 WeatherBean 实例并向它植入模型数据。最后,createBean 方法把 bean 插入到请求中,使它能被视图 JSP 使用。

接下来的两个类(图 12)提供每种视图类型的控制器。第一个是 HTML 控制器,第二个是 WML 控制器。这两个控制器类都继承控制器基类来继承后者提供的通用功能。为简单起见,除调用的视图不同之外,这两个控制器之间并没有太多不同之处。但您可以考虑根据用户所采取的操作提供为每种设备类型定制的特别功能。例如,一个胖客户机控制器可以提供对图标的拖放控制,而瘦客户机控制器却不会。

为使这些控制器能工作,您需要向 portlet web.xml 文件注册它们,方法是添加下列配置标志。( web.xmlportlet.xml 文件的详细信息不在本示例内。要了解更多信息,请参阅 参考资料部分的 WebSphere Portal InfoCenter 文档。)

图 12. HTML 与 WML 控制器

...
  <servlet-name>WeatherPortlet</servlet-name>
  <servlet-class>com.ibm.wps.portlets.MVCPortlet</servlet-class>
  <init-param>
    <param-name>controller.html</param-name>
    <param-value>com.ibm.wps.samples.weather.WeatherHTMLController</param-value>
  </init-param>
  
  <init-param>
    <param-name>controller.wml</param-name>
    <param-value>com.ibm.wps.samples.weather.WeatherWMLController</param-value>
  </init-param>
  
  <init-param>
    <param-name>view.HTML</param-name>
    <param-value>DisplayWeatherHTML.jsp</param-value>
  </init-param>
  <init-param>
    <param-name>view.WML</param-name>
    <param-value>DisplayWeatherWML.jsp</param-value>
  </init-param>
  <init-param>
    <param-name>ZipCode</param-name>
    <param-value>27709</param-value>
  </init-param>
...

现在,我们需要提供两个视图 JSP,一个用于 HTML,另一个用于 WML(图 13)。由于 HTML 视图是用于胖客户机的,我们将在其中显示用户名,但在 WML 视图中将省略用户名,因为这个是更受限制的客户机。

图 13. HTML 与 WML JSP

<!----------------------------------------------------------------------------->
<!-- DisplayWeatherHTML.jsp - A JSP view for rendering HTML.                 -->
<!----------------------------------------------------------------------------->
<%@ page contentType="text/html" errorPage="" %>
<jsp:useBean id="weatherBean"
     class="com.ibm.wps.samples.weather.WeatherBean"
     scope="request"/>
<p>
Hi <%= weatherBean.getFullName() == null ? "" : weatherBean.getFullName() %>!
<br>
The current temperature in your area is <%= weatherBean.getTemperature() %>.
<!----------------------------------------------------------------------------->
<!-- DisplayWeatherWML.jsp - A JSP view for rendering WML.                   -->
<!----------------------------------------------------------------------------->
<%@ page contentType="text/wml" errorPage="" %>
<jsp:useBean id="weatherBean"
     class="com.ibm.wps.samples.weather.WeatherBean"
     scope="request"/>
<p align="left">
Current temperature is <%= weatherBean.getTemperature() %>.
</p>

图 14 和图 15 分别显示了如何在浏览器和智能电话上处理这些 portlet。

图 14. 使用 HTML JSP 的 Weather Portlet
使用 HTML JSP 的 Weather Portlet
使用 HTML JSP 的 Weather Portlet
图 15. 使用 WML JSP 的 Weather Portlet
使用 WML JSP 的 Weather Portlet

设备功能

到目前为止,本文只描述了如何为种类繁多的设备提供视图。然而,假设许多种设备具有相同的功能通常并不妥当,对于移动设备尤其如此。例如,一些智能电话支持 WML 版本 1.1,而其它智能电话支持 WML 1.2(例如,不支持表)的子集,另有一些智能电话则支持整个 WML 1.2 规范。那么,您如何提供一个视图,使它的智能程度足够高,能够考虑进所有这些设备的多样性,而不必求助于那些只支持所有设备都有的功能的设备?

IBM WebSphere Portal API 提供了一种机制供 portlet 查询 portlet 视图的目标设备的功能。门户不是要求每个 portlet 都理解所有用户代理(User-Agent)类型的功能,而是提供用户代理和设备功能之间的一个抽象。门户如何确定设备的功能呢?当向门户发送一个 HTTP 请求时,发出请求的设备,即用户代理发送一个 HTTP 域,它包含关于发出请求的设备的信息的令牌。例如,一个 Nokia Communicator 9110 发送下列用户代理域:

图 16. 用户代理域样本

Nokia-Communicator-WWW-browser/3.0 (Geos 3.0 Nokia-9110)

门户在内部维护从用户代理域到期望的设备功能的映射(请参阅 参考资料部分的 WebSphere Portal InfoCenter)。这样,门户就能够推断设备的功能,并向 portlet 提供这些信息。这样就可以使用可伸缩的设计了,在这种设计中,新的用户代理映射可以作为新设备添加到门户,但 portlet 不需要改变自己的行为。而是使用相同的设备能力抽象来推断出应该如何处理某个视图。

这个能力抽象类,适当的叫法是“功能“(Capability),是 org.apache.jetspeed.portlet 包的一部分。它提供一般的功能属性,比如,支持的是哪种级别的标志(例如,WML 1.1 还是 WML 1.2),和更具体的功能,象支持的是哪种具体类型的函数(例如,支持 JavaScript 还是不支持 JavaScript)。在我们的天气示例中,我们可以对 WML JSP 进行修改,使它考虑进 WML 客户机的能力。如果客户机不支持 WML 表,那么将直接输出文本。否则,将输出分行列出用户名和当前温度的表,如图 17 所示。

图 17. 使用设备功能的 WML JSP

<!----------------------------------------------------------------------------->
<!-- DisplayWeatherWML.jsp - A JSP view for rendering WML using the          -->
<!-- capabilities of the device.                                             -->
<!----------------------------------------------------------------------------->
<%@ page contentType="text/wml" errorPage="" %>
<%@ page import="org.apache.jetspeed.portlet.*" %>
<jsp:useBean id="weatherBean"
     class="com.ibm.wps.samples.weather.WeatherBean"
     scope="request"/>
<!-- Add statements so that we can access the portlet request/response. -->
<%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %>
<portletAPI:init />
<p align="left">
  <% if (portletRequest.getClient().isCapableOf(Capability.WML_TABLE)) { %>
    <table columns="1">
      <tr><td>
        Hi <%= weatherBean.getFullName() == null ? "" :
               weatherBean.getFullName() %>!
      </td></tr>
      <tr><td>          
        Current temperature is <%= weatherBean.getTemperature() %>.
      </td></tr>
    </table>
  <% } else { %>
    Current temperature is <%= weatherBean.getTemperature() %>.
  <% } %>
</p>

对于不支持 WML 表的客户机电话,它的输出将与我们前面的 JSP 示例的输出一样。对于确实支持 WML 表的电话,输出将与下面的图 18 相似。

图 18. 使用支持表的 WML JSP 的 Weather Portlet
使用支持表的 WML JSP 的 Weather Portlet

一种样式 bean 解决方案

本文的最后一个论题集中在更高级的功能属性解决方案上。我们将要使用的示例与客户机设备是否支持级联样式表(Cascading Style Sheet,CSS)有关。这个属性很重要,因为门户要使用 CSS 把主题(theme)和皮肤应用到整个门户的外观和感觉。我们建议 portlet 开发者参照已发布的 portlet 样式指南(请参阅 参考资料)的要求,把 CSS 类标识符放在它们的 HTML 中,这样,例如当字体颜色改变时,portlet 就会应用这种颜色改变。图 19 中的 HTML 内容片段演示了如何使用 portlet 文本的字体样式的样式指南:

图 19. 使用样式指南

...
<font class="wpsPortletText">
  Current temperature is <%= weatherBean.getTemperature() %>.
</font>
...

使用 CSS 类标识符 wpsPortletText 时,如果客户机设备应用它,那么 portlet 将继承 CSS 样式。

许多新的中等胖客户机面临的问题是目前它们只支持一个有限的 HTML 子集,这个子集并不包括 CSS 支持,这样它们就不可能在客户机上应用样式表。因此,使用这个指南写的 portlet 就不能正确地显示。

一个可能的解决方案是为视图提供另一种级别的抽象。这个抽象可以采取视图可以使用的样式 bean 的形式来根据客户机的功能适当地对内容进行格式化。对于我们的示例设备(该设备不支持 CSS),我们需要把样式的值直接插入到 JSP 中。但是,如果设备的确支持 CSS,视图就应该添加推荐的样式指南类标识符,如图 20 所示。

图 20. 基于客户机功能的样式 bean

...
<!-- For a device that support CSS. --> 
<font class="wpsPortletText">
  Current temperature is <%= weatherBean.getTemperature() %>.
</font>
...
...
<!-- For a device that does NOT support CSS. -->
        
<font size="1" face="sans-serif">
  Current temperature is <%= weatherBean.getTemperature() %>.
</font>
...

为创建这种级别的抽象,为 portlet 视图 JSP 定义了图 21 中的 bean API,portlet 视图 JSP 用这个 bean API 来确定应该为内容使用哪种字体样式。

图 21. portlet 视图 JSP 的样本 API

/**
 * StyleBean.java
 * An abstraction layer for creating a views based on the capabilities of a 
 * client.  For example, CSS support vs. no CSS support.
 */
public abstract class StyleBean {
  /**
   * Initialize the bean.
   * @param   request  The portlet request object.
   */
  public abstract void init(PortletRequest request);
  /**
   * Method to call to insert the font style guideline content.
   * @param   styleClass  The name of the style class.
   * @return  The markup to insert into the JSP for starting a font.
   */
  public abstract String startFont(String styleClass);
  /**
   * Method to call to insert the font style guideline content.
   * @param   styleClass  The name of the style class.
   * @return  The markup to insert into the JSP for ending a font.
   */
  public abstract String endFont(String styleClass);
}

现在,HTML 视图只需使用这个 bean 即可处理所需的内容,如图 22 所示。

图 22. DisplayWeather 代码样本

<!----------------------------------------------------------------------------->
<!-- DisplayWeatherHTML.jsp - A JSP view for rendering HTML for both         -->
<!-- browsers and Pocket PC devices.                                         -->
<!----------------------------------------------------------------------------->
<%@ page contentType="text/html" errorPage="" %>
<%@ page import="org.apache.jetspeed.portlet.*" %>
<%@ page import="com.ibm.wps.style.*" %>
<jsp:useBean id="weatherBean"
     class="com.ibm.wps.samples.weather.WeatherBean"
     scope="request"/>
<jsp:useBean id="styleBean"
     class="com.ibm.wps.style.StyleBean"
     scope="request"/>
<!-- Add statements so that we can access the portlet request/response. -->
<%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %>
<portletAPI:init />
<!-- Initialize the style bean. -->
<% styleBean.init(portletRequest); %>
<!----------------------------------------------------------------------------->
<p>
<%= styleBean.startFont("wpsPortletText") %>
  The current temperature in your area is <%= weatherBean.getTemperature() %>.
<%= styleBean.endFont("wpsPortletText") %>
</p>

bean 自己将负责根据客户机是否支持 CSS 来决定处理哪些内容。这只是 bean 的用途的一个示例。bean API 可以扩展为支持更多的标志标记,比如表头、背景色等等。图 23 中的代码显示如何使用门户 API 实现 StyleBean。

图 23. 使用门户 API 实现 StyleBean

package com.ibm.wps.style;
import java.io.*;
import java.util.*;
import org.apache.jetspeed.portlet.*;
public class StyleBean {
  /** The resource that defines the style values. */
  private ResourceBundle resource = null;
  public void init(PortletRequest request) {
    // Determine whether the client device supports CSS and if not,
    // then load the resource style values.
    if (!request.getClient().isCapableOf(Capability.HTML_CSS)) {
      
      try {
    
        // Retrieve the resource bundle from the classpath.
        resource = ResourceBundle.getBundle("com.ibm.wps.style.NoCSS");
      }
      catch (Exception e) {
        // Resource bundle not available.
        resource = null;
    }}      
  }
  public String startFont(String styleClass) {
    // If the client supports CSS, use class identifier.
    if (resource == null) {
      return "<font class=\"" + styleClass + "\">";
    }
    // If the client does not support CSS, use the style values.
    String markup = "<font size=\"" +
                    resource.getString(styleClass + ".font.size") +
                    "\" face=\"" +
                    resource.getString(styleClass + ".font.face") +
                    "\">";
    return markup;
  }
  public String endFont(String styleClass) {
    return "</font>";
  }
}

StyleBean 使用加载的属性文件作为 Java 资源束,以便确定样式属性的值。这意味着资源束必须与 StyleBean 打包在一起,或者至少要在同一个类路径下。下面是 NoCSS.properties 文件的内容:

图 24. NoCSS 属性文件

wpsPortletText.font.size = 1
wpsPortletText.font.face = sans-serif

不必更改代码就可以修改 NoCSS.properties 文件,为不支持 CSS 的设备修改样式。bean 目前只提供字体样式的抽象,但为其它样式类型扩展接口比较容易。尽管这个示例中没显示,但修改 StyleBean 类,从而使 init 方法可以打开恰当的 CSS,而不必引用属性资源束,这也是可能的。init 方法然后将直接解析出 wpsPortletText 样式属性,并使用那些属性而不是使用属性资源束,从而确保 portlet 中使用的样式始终与同样支持 CSS 的其它 HTML 设备一致。

结束语

本文只是讨论了普及的 portlet 编程巨大潜能的一些表层知识。在开始学习 portlet 开发的过程中,使用了模型-视图-控制器范例和普及的 MVC API,并讨论了如何处理细粒度的设备功能和解决与缺少级联样式表支持相关的问题的可能解决方案,本文中的概念旨在让您感受一下普及的 portlet 编程的基本结构。祝您在将这些想法应用到自己的开发项目中的过程中一路好运。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=55529
ArticleTitle=WebSphere Portal 编程: 普及的 Portlet 开发
publish-date=10082002