IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Information Management | Lotus  >

开发一个 IBM Mashup Center 插件用于将 HTML 转换成 XML

详细介绍开发 IBM Mashup Center 插件

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

讨论

样例代码

英文原文

英文原文


级别: 高级

Louis MauIBM

2009 年 10 月 30 日

学习如何为 IBM® Mashup Center 构建一个插件,用于将 HTML 转换成 XML,从而为使用 Feed Mashup Editor 从 HTML 页面抽取数据打开方便之门。

引言

IBM Mashup Center 带有提要生成器,可以直接从很多企业数据源访问和生成 XML 提要。与此同时,由于数据存储和软件的多样性,也会有一些数据源不能被这些内置的生成器访问。要扩大 IBM Mashup Center 的功能,可以通过增加插件来扩展提要生成功能。

本文是文章 “ Extend the reach of data for IBM Mashup Center” 的续篇,并且基于该软件的 V1.1。本文假设您已经熟悉编写 IBM Mashup Center 插件的基本知识。具体来说,应该知道如何用 Java™、JSP 和 JavaScript 编程。本文展示如何开发一个插件来将 HTML 转换成 XML,并使用这个例子演示编写更复杂的插件。一个额外的好处是, HTML 一旦转换成 XML 格式,就可被读取到 Feed Mashup Editor,从而允许数据抽取。





回页首


用于将 HTML 转换成 XML 的工具

我们需要一个 Java 包来将 HTML 转换成 XML。存在很多这样的 Java 包。本项目使用 JTidy,这是一个来自 W3C 的 HTML Tidy 的 Java 端口。HTML Tidy 开始是作为 HTML 语法检查器和美编工具(pretty printer)。JTidy 跟它的非 Java 版本一样,可用作整理 HTML 的工具。此外,它还支持 XML 的生成。在 参考资料 小节中可以找到一个链接,下载并安装 JTidy。

我们将使用 build 文件夹中的 Tidy.jar。为了让 JAR 自包含,它包含一个较老版本的 W3C Document Object Model (DOM) 类。由于现在 JDK 包含更新版本的 W3C DOM 类,所以应该删除 org.w3c.dom 包中的类。





回页首


设置 Eclipse 项目

正如 Application Programming Interface Reference, Version 1.0 的第 6.1 节中所描述的,提要生成框架自动搜索 ZIP 文件,其中包含放置在特殊文件夹 <WebApplication>/WEB-INF/plugins 中的第三方插件。ZIP 归档文件必须具有以下指定的文件夹结构:

  • /client/plugins/PLUGIN_DIR -- 包含用于浏览器的文件,比如图像、JavaScript 文件,等等。
  • /server/plugins/PLUGIN_DIR -- 包含由插件用于呈现其自己的文件(HTML 文件、JSP 页面)。还可以包含放置插件文件的额外文件夹。
  • /WEB-INF/classes -- 包含插件 Java 类。这可以是一个文件夹层次。类将被复制到 <WebApplication>/WEB-INF/classes 中。
  • /WEB-INF/lib -- 包含插件(第三方)使用的 JAR 文件。

为了简化插件最终的编译和打包,您可能想要使用自己喜欢的 IDE 创建一个项目,其中具有与最终 ZIP 归档文件所需的一致的目录结构。图 1 展示了作者创建的 Eclipse 项目的布局。


图 1. Eclipse 项目
展示 Eclipse 项目的层次结构

注意,PLUGIN_DIR 必须是唯一的,并且与插件的包名称相同。本例我使用的包名称是 sample.mashupcenter.tidyhtml。还可以看到,我们将 Tidy.jar 放置在 WEB-INF/lib 文件夹中,并创建了一个名为 lib_noship 的文件夹,其中包含两个额外的 JAR,由 Mashup Center 服务器在运行时期间提供,在开发环境中不可用。这些 JAR 应该不包含在最终的部署 ZIP 文件中。(实际上,如果错误地包含了这些 JAR,插件 ZIP 文件的加载就会失败。)





回页首


plugin.xml 文件

每个 Mashup Center 插件都具有两个主要的操作:一个编辑器用于收集创建参数,一个生成器用于创建提要。(提要通常是指一个遵循 RSS 或 ATOM 规范的 xml 文档。注意,在本例中,生成的 XML 将源自 HTML 并会遵从 RSS 或 ATOM 规范。)插件框架通过读取 package.xml 文件(该文件必须放置在 server/plugins/PLUGIN_DIR 文件夹中),找出由哪些 Java 类实现这两个关键操作。清单 1 展示了该插件的 package.xml 文件。


清单 1. 包 XML 文件
<plugin>
  <name>Tidy Html</name>
  <author>L. Mau</author>
  <version>1.0</version>
  <category>departmental</category>
  <editor>Html2XmlEditorPlugin</editor>
  <generator>Html2XmlGeneratorPlugin</generator>
  <description>convert any Html page into xhtml</description>
  <icon16path>/plugins/sample.mashupcenter.tidyhtml/icons/btn16_hello.gif</
	icon16path>
  <icon32path>/plugins/sample.mashupcenter.tidyhtml/icons/btn32_hello.gif</
	icon32path>
  <icon64path>/plugins/sample.mashupcenter.tidyhtml/icons/btn64_hello.gif</
	icon64path>
  <objectType>feed</objectType>
</plugin>

有必要说一下,名称、描述和版本元素是为了方便创建者,并不被 IBM Mashup Center 插件框架所使用。当用户选择 New Feed 之后出现选项列表时,插件框架使用 ui.properties 文件中的 plugin.uiname 属性作为插件的名称。

ui.properties 文件放置在 /server/plugins/PLUGIN_DIR/nls 文件夹中,使用标准的 Java 资源束加载约定进行加载。对于每种受支持的语言,plugin.name 转换后的字符串放置在一个属性文件中,“ui.”后附加场所信息。例如,该文件的日文版本应该命名为 ui_ja.properties。





回页首


实现编辑器

Html2XmlEditorPlugin 扩展 BaseEditorPlugin,后者是一个基类,需要我们实现 renderEditor 方法。


清单 2. Html2XmlEditorPlugin 类
package sample.mashupcenter.tidyhtml;

import : : : : :   // omitted from listing

/**
 * This plugin uses JTidy to convert Html to Xml.
 */
public class Html2XmlEditorPlugin extends BaseEditorPlugin {

    private static final Log log = LogFactory.getLog(Html2XmlEditorPlugin.class);

    public  static final String I18N_RESFILE = Html2XmlConstants.PLUGIN_NAME
                                         + ".nls.tidyhtml";
    public  static final String HTTP_BASEURL = "plugins/"
                                         + Html2XmlConstants.PLUGIN_NAME +  "/";
    public  static final String RES_BASEURL  = "/" + HTTP_BASEURL;
    public  static final String HELPPATH     = HTTP_BASEURL + "help/tidyhtml.htm";

类中的第一个语句从 Apache 公共日志包创建一个静态日志实例。这与提要生成框架使用的是相同的日志基础设施。日志消息将被提要生成框架的日志基础设施交叉存取,并被写入文件 <WebApplication>/META-INF/logs/javamashuphub.log。默认情况下,只有来自 WARN 及以上(例如 ERROR)的消息将被写入日志文件。

下一个语句是资源文件的字符串常量。即使不需要将所有的字符串常量都转换成不同的语言,但是将用户界面显示的所有文本字符串都保存在单独的资源文件中仍然是一个良好的编程习惯。注意,文件 tidyhtml.properties 与前面介绍过的 ui.properties 文件放置在相同的目录中。

最后几个字符串常量定义到插件所需各种资源的路径。注意,这些资源的运行时位置模拟插件 ZIP 文件的结构。





回页首


renderEditor 方法

renderEditor 方法有两个参数,即 RequestData 和 Entry。在用户选择使用该插件创建一个新提要时,或者在用户编辑一个由该插件以前创建的现有提要时,框架将调用该方法。我们将会看到,该方法接受类型为 RequestData 和 Entry 的两个参数。这两个参数实际上对于框架为响应用户动作而调用的所有方法是共同的。RequestData 包含从浏览器发送来的信息,Entry 包含框架为这个提要实例而维护的所有信息。


清单 3. renderEditor 方法体
public ViewBean renderEditor(RequestData rdata, Entry entry)
{
    ResourceBundle i18n = ResourceBundle.getBundle(I18N_RESFILE,rdata.getLocale());
    String pluginId = this.getId();

    Html2XmlUrlViewBean htViewBean = new Html2XmlUrlViewBean();
    htViewBean.setEntry(entry);
    htViewBean.setHtmlUrl( entry.getAttribute(Html2XmlUrlViewBean.PARAM_HTMLURL ) );
    htViewBean.setSnapshot( entry.getAttribute(Html2XmlUrlViewBean.PARAM_SNAPSHOT ) );

    FormViewBean form = new FormViewBean();
    form.setSuffix( htViewBean.getSuffix() );
    form.addComponent( htViewBean );
    form.setOnsubmit( PluginHelper.getClientMe(pluginId, entry.getObjectId()) +
            ".invokeServer('displayHtmlPage',"  +
            PluginHelper.getClientId(pluginId, entry.getObjectId()) +
            "_" + form.getSuffix() + ");");
    form.setEntry(entry);

    FrameViewBean frame = new FrameViewBean();
    frame.addComponent(form);
    frame.setLabel(entry.getTitle());
    frame.setTitle(i18n.getString("frame.urltitle"));
    frame.setEntry(entry);
    frame.setHelpPath( HELPPATH );
    return frame;
}

该方法返回类型 ViewBean 的一个实例。ViewBean 类似于 Java Bean,具有针对显示属性的 getter 和 setter 方法。它的主要目的是指定提要生成框架使用的 JSP,以便为插件特定的编辑器创建 HTML。由于 renderEditor 方法会被调用来编辑现有的实例,所以它会通过调用 Entry::getAttribute 方法,检索以前为该实例保存的任何数据。后面我们将看到这些数据是何时以及如何保存的。检索到的值然后被传递到 Html2XmlUrlViewBean,以便相关的 JSP 可以显示用户以前提供的值。注意,插件特定的 Html2XmlViewBean 并不直接返回,而是通过 addComponent 方法包装在 FormViewBean 实例中。FormViewBean 提供定制的 JavaScript 逻辑,当向导似的编辑器界面中的 Next 按钮被点击时,将用户输入的信息发送到插件。最后,FormViewBean 又被包装到 FrameViewBean 中。返回的是后者。

在继续介绍之前,还需指出最后一点。我们调用 setOnsubmit 方法来提供 Next 按钮被点击时执行的大块 JavaScript 代码。JavaScript 代码调用 Application Programming Interface Reference 的第 6.3.2 节介绍的 hub.managers.InvokePlugin 的 invokeServer 函数。第一个参数指定这个类中的 displayHtmlPage 方法,该方法将用于提供下一个编辑器页面。





回页首


Html2XmlUrlViewBean 及其相关的 JSP 文件

前一节我们已经简要描述了 Html2XmlUrlViewBean。清单 4 展示了类定义的一部分:


清单 4. Html2XmlUrlViewBean
public class Html2XmlUrlViewBean extends ViewBean
{
  public static final String PARAM_HTMLURL     = "htmlurl";
  public static final String PARAM_SNAPSHOT    = "snapshot";
	
  private String  htmlUrl;
  private String  snapshot;

  public Html2XmlUrlViewBean()
 {
    this.setI18NProperties( Html2XmlConstants.PLUGIN_NAME + ".nls.tidyhtml");
 }

  /* (non-Javadoc)
   * @see com.ibm.mashuphub.model.ViewBean#getJSPPath()
   */
  @Override
  public String getJSPPath() {
    return "/server/plugins/" + Html2XmlConstants.PLUGIN_NAME + "/tidyhtmlUrl.jsp";
 }

 public String getSuffix() {
    return "tidyhtmlUrl";
 }

getJSPPath 方法将由 FormViewBean 在试图为收集这些插件特定的参数生成 HTML 表单时进行调用。getSuffix 方法应该从该插件返回各个 ViewBeans 独特的字符串。在了解相关的 JSP 文件之前,首先来看一下所呈现的 HTML 表单是有帮助的:


图 2. InfoSphere MashupHug 首个编辑器页面
带有输入 url 的页面截图

注意表单具有两个输入元素:

  • 一个文本字段,用于收集用户想要转换成 XML 的 HTML 的 URL。
  • 一个检查框,指出我们将保存第一次调用时生成的 XML,并在后续的提要生成请求中返回 XML。当 HTML 页面是静态的,很少改变时,适合使用该选项。

既然已经知道将要生成什么,理解 JSP 文件就容易多了。


清单 5. tidyhtmlUrl.jsp
<%@page import="sample.mashupcenter.tidyhtml.Html2XmlUrlViewBean"%>
<%
    Html2XmlUrlViewBean htViewBean = new Html2XmlUrlViewBean();
    htViewBean = (Html2XmlUrlViewBean) htViewBean.getViewBeanFromRequest(request);
ResourceBundle i18n = ResourceBundle.getBundle(htViewBean.getI18NProperties(),
                                               request.getLocale());

    String objectId = htViewBean.getEntry().getObjectId();
String id = com.ibm.mashuphub.helper.PluginHelper.getClientId(
                                            htViewBean.getPluginId(), objectId);
%>

<br/>

<label for='htmlurl'><%=i18n.getString("form.htmlurl.label") %></label>
<div  class="rightCol">
   <input type='text'
          id='<%=id%>_htmlurl'
          name='<%= Html2XmlUrlViewBean.PARAM_HTMLURL %>'
          value='<%= htViewBean.getHtmlUrl() %>'
          maxlength='256' style='width=600px;' />
</div>

<div   class="rightCol">
   <input type='checkbox'
          id='<%=id%>_snapshot'
          name='<%= Html2XmlUrlViewBean.PARAM_SNAPSHOT %>'
          value='y'
          <%= "y".equals(htViewBean.getSnapshot()) ? "checked" : "" %>  />
   <%= i18n.getString("form.snapshot.label") %>
</div>

忽略 import 语句,前两个语句的目的是检索与 JSP 相关的 ViewBean。这与 JSP 通常检索它们相关的 Java bean 的方式稍有不同,那是从请求对象进行检索。对应于两个表单输入元素,分别有两个文本和检查框类型的 HTML 输入元素。注意,我们使用类 Html2XmlUrlViewBean 中的常量 PARAM_HTMLURL 和 PARAM_SNAPSHOT 来命名两个输入元素。这些名称将作为名称出现在点击 Next 按钮时发送的 URL 查询字符串中。使用字符串常量是最佳方式,可以确保它们完全与服务器期待的一致。最终,我们使用以前用 renderEditor 方法检索的值初始化这些输入元素。





回页首


DisplayHtmlPage 方法

我在前面的小节中曾经提到过,Html2XMLEditorPlugin 类中的 displayHtmlPage 方法将被用于提供下一个编辑器页面。方法 displayHtmlPage 不是从基类 BaseEditorPlugin 继承而来,它接受两个 RequestData 和 Entry 类型的参数。一个 EditorPlugin 可以引入任意多个具有相同签名的公共方法。所有这些方法可由运行在浏览器上的客户端通过一个 AJAX 调用进行调用。


清单 6. displayHtmlPage 方法
public  ViewBean  displayHtmlPage(RequestData rdata, Entry entry)
{
    ResourceBundle i18n = ResourceBundle.getBundle(I18N_RESFILE,rdata.getLocale());
    String pluginId = this.getId();

    // do not use "url" since the latter got intercepted in RequestData.init();
    String  sHtmlUrl  = rdata.getParameter( Html2XmlUrlViewBean.PARAM_HTMLURL );
    String  snapshot  = rdata.getParameter( Html2XmlUrlViewBean.PARAM_SNAPSHOT );
    log.debug("snapshot,sHtml=" + snapshot + "," + sHtmlUrl );

    Html2XmlContentViewBean htViewBean = new Html2XmlContentViewBean();
    htViewBean.setEntry(entry);
    htViewBean.setHtmlUrl( sHtmlUrl );
    htViewBean.setSnapshot( snapshot );

    FormViewBean form = new FormViewBean();
    form.setSuffix( htViewBean.getSuffix() );
    form.addComponent( htViewBean );
form.setOnsubmit(PluginHelper.getClientMe(pluginId,
                                          entry.getObjectId())+".submit();");
    form.setEntry(entry); // must be set, used to init JS plugin object

    FrameViewBean frame = new FrameViewBean();
    frame.addComponent(form);
    frame.setLabel(entry.getTitle());
    frame.setTitle(i18n.getString("frame.tabtitle"));
    frame.setEntry(entry);
    frame.setHelpPath( HELPPATH );

    JSONAJAXResponseViewBean ajaxViewBean = new JSONAJAXResponseViewBean();
    ajaxViewBean.setMethod(JSONAJAXResponseViewBean.METHOD_SHOW_EDITOR);
    ajaxViewBean.setCode( JSONAJAXResponseViewBean.PAGE_CONTENT );
    ajaxViewBean.addComponent(frame);
    return ajaxViewBean;
}

该方法的目的是为用户呈现第二个编辑器页面,以验证检索到的 HTML 的内容。因此,返回类型必须是 ViewBean。displayHtmlPage 方法中的逻辑类似于我们前面讨论的 renderEditor 方法,但是有三个明显的区别:

  • 不是从 Entry 实例检索以前输入的配置值,我们是通过调用 RequestData 的 getParameter 方法检索用户在该编辑会话期间输入的值。这些参数对应于 AJAX 调用发送到服务器的 JSP 表单中的输入元素。
  • 每个页面需要一个不同的 ViewBean。该方法实例化 Html2XmlContentViewBean 的一个实例。跟以前一样,它必须包装在一个 FormViewBean 中,这是一个 FrameViewBean 链。此外,我们需要进一步将 FrameViewBean 包装在一个 JSONAJAXResponseViewBean 实例中。后者自动发生在 renderEditor 方法中,但是在这里需要明确地完成。
  • 由于我们将提供我们自己的 JavaScript,所以我们在传递到 setOnsubmit 方法的 JavaScript 中展示一个稍微不同的变体。不是直接调用 invokeServer,我们将在相关的 JavaScript 中调用 submit 方法。

另外一个值得指出的细节是,调用静态日志记录器实例以记录用户特定的参数,帮助确定问题。





回页首


Html2XmlContentViewBean 和相关的 JSP

Html2XmlContentViewBean 相当简单,基本上只是从我们前面看到过的 Html2XmlUrlViewBean 返回一个不同的 JSP 路径和后缀。读者可以通过下载 附加的包 了解它,我们后面不再详述。要生成的编辑器页面也很简单,包含一个区域用于显示检索到的 HTML。下面的屏幕快照展示了显示区域的一角:


图 3. Preview HTML 内容页面
InfoSphere MashupHub 截屏,展示 HTML 内容页面的一角

下面我们来看相关 JSP 文件 tidyhtmlContent.jsp。可以看到,为了生成显示区域,相关 JSP 只是在 JSP 文件的底部包含单个 div 元素。由于我们后面要用到 id 属性,所以这里是讨论它的构造的好地方。

id 属性必须在浏览器窗口中的所有 HTML 元素中是唯一的。使用 id,浏览器提供的 API 可以检索 HTML 元素作为 JavaScript DOM 对象,允许动态操作。由于一个用户可以具有在相同时间打开的给定插件编辑器的多个实例,所以 JSP 模板中的 HTML 元素将被多次实例化。为确保此类元素的 id 是唯一的,我们调用 PluginHelper 的 getClientId 方法来检索唯一的提要实例 id,并将之附加到 id。


清单 7. tidyhtmlContent.jsp
<%@page import="sample.mashupcenter.tidyhtml.Html2XmlContentViewBean"%>
<%
    Html2XmlContentViewBean htViewBean = new Html2XmlContentViewBean();
    htViewBean = (Html2XmlContentViewBean) htViewBean.getViewBeanFromRequest(request);
ResourceBundle i18n = ResourceBundle.getBundle(htViewBean.getI18NProperties(),
                                               request.getLocale());

    String objectId = htViewBean.getEntry().getObjectId();
String id = com.ibm.mashuphub.helper.PluginHelper.getClientId(
                                             htViewBean.getPluginId(), objectId);
String me = com.ibm.mashuphub.helper.PluginHelper.getClientMe(
                                             htViewBean.getPluginId(), objectId);

    String   snapshot    = "\"" + htViewBean.getSnapshot() + "\"";
    String   htmlUrl     = htViewBean.getHtmlUrl();
    htmlUrl     = ( htmlUrl == null ?  "\"\""   :  "\"" + htmlUrl + "\"" ); 
%>
<script type="text/javascript">

    dojo.registerModulePath("plugins.tidyhtml" ,
                 "../../../../client/plugins/sample.mashupcenter.tidyhtml/script");
    dojo.require("plugins.tidyhtml.PreviewHtml");

    new plugins.tidyhtml.PreviewHtml(
               <%= me %>.plugin_id,
               <%= me %>.entry_id,
               <%= me %>.workflow);

    <%=me%>.init( <%= htmlUrl %> , <%= snapshot %> );
    <%=me%>.onLoadEditor();

</script>

<div id='<%=id%>_htmlContent' style='width:100%;
     overflow:auto; border: 2px  solid #000000;'>
</div>

该 JSP 的一个新方面是包含将运行在客户端上的自定义 JavaScript。IBM Mashup Center 提要生成框架使用 Dojo AJAX 包。参见 参考资料 小节,获得到 Dojo 文档的链接。我们将在我们自定义的 JavaScript 中使用 Dojo AJAX 包。大多数自定义 JavaScript 驻留在一个名为 plugins.tidyhtml.PreviewHtml 的 Dojo 类中。

要使用它,我们需要用一个 dojo.require 函数调用导入它。Dojo registerModulePath 函数调用告诉 Dojo 如何从模块 plugins.tidyhtml 定位类。注意,指定的路径相对于 Dojo 包所在的位置,因此需要向后引用“../../../..”。以上初始化逻辑内联生成在一个脚本标签中。此外,内联的 JavaScript 创建 PreviewHtml 类的一个实例,并调用它的 init 和 onLoadEditor 方法。下一节将更详细地讨论 PreviewHtml 类。





回页首


PreviewHtml Dojo 类

PreviewHtml Dojo 类继承自 hub.managers.InvokePlugin 类,后者是客户端提要生成框架的一部分。InvokePlugin 类在 Application Programming Interface Reference, Version 1.0 的第 6.3.2 节中更详细地描述。PreviewHtml Dojo 类中重要的方法是 onLoadEditor 和 populateContent。


清单 8. PreviewHtml Dojo 类
onLoadEditor: function()
{
    this.id = this.getEditorId();
    this.htmlContentNode = dojo.byId( this.id + '_htmlContent' );

    this.populateContent();
},

populateContent: function( )
{
    console.log( "populateContent called" );

    var baseUrl = hub.urls.getAjaxUrl( this.plugin_id,this.entry_id, 'getHtmlContent');
    var htmlurl = baseUrl + "?htmlurl="   + escape( this.htmlUrl   );
    if ( this.htmlContentInternalNode )
        this.htmlContentNode.removeChild( this.htmlContentInternalNode );
    this.htmlContentInternalNode = document.createElement( 'iframe' );
    this.htmlContentInternalNode.setAttribute( "src", htmlurl ); 
    this.htmlContentInternalNode.setAttribute( "width", "100%" );
    this.htmlContentInternalNode.setAttribute( "height", "400px" );  
    this.htmlContentNode.appendChild( this.htmlContentInternalNode );
},

函数 populateContent 由 onLoadEditor 在页面加载期间调用。它动态地创建一个 iframe 来显示检索到的 HTML,HTML 用于定位任何包含的样式表和脚本的效果,放置它们影响其他页面的出现。动态创建的 iframe 被附加到 JSP 创建的静态 div。为了检索显示区域对应的 DOM 节点,我们使用通过附加唯一的提要实例 id 到公共后缀而生成的 div 元素的唯一 id。

在服务器端,我们使用 PluginHelper 类上的一个方法来获得唯一的提要实例 id。在浏览器端,我们从 PreviewHtml Dojo 类的父类(即 hub.managers.InvokePlugin)调用 getEditorId 函数。为了检索 HTML 内容,我们将利用 Iframe "src" 属性的优势。iframe 将自动检索并显示初始化期间由 src 属性所指向的内容。我们设置 src 属性来调用编辑器插件 getHtmlContent 方法。注意我们通过调用 getAjaxUrl 函数并将结果附加到字符串“getHtmlContent”而创建 URL 的方式。





回页首


用于检索 HTML 的 AJAX 方法

我在前面小节中提到过,任何以 RequestData 和 Entry 作为参数的公共方法都可能使用 AJAX 调用而被调用。具体来说,方法 getHtmlContent 可被 PreviewHtml Dojo 类调用,以从用户提供的 URL 返回 HTML。由于实际的 HTML 检索对于提要生成很常见,将放在稍后介绍,所以这里我将不提供任何代码片段。我唯一想要指出的一点是方法的返回类型。在前面的例子中,AJAX 方法 displayHtmlPage 返回一个 ViewBean。AJAX 方法一般可以返回任何对象,并且它的 toString 值也会返回。参见 Application Programming Interface Reference, Version 1.0 的第 6.3.2 节。





回页首


我们的最后一个编辑器方法:saveFeedEntry

saveFeedEntry 是 Html2XmlEditorPlugin 的另一个公共方法,由 AJAX 调用来处理编辑过程中的最后一步,即保存用户输入的内容。它类似于其他插件中的保存方法。新内容是“资源”处理。资源不同于大小和类型中的属性。资源可以是二进制的,大小上最大可达到 1GB。不同的是,属性只限于是大小为 10MB 的字符串。属性的大小限制对于内容应该足够了,但是出于演示目的,我们会将 HTML 内容保存为资源。当快照选项被选中时,生成器将只从以前指定的 url 检索 HTML 内容。HTML 内容然后被转换成 XML 并保存。所有后续的提要生成请求都将从所保存的 XML 得到满足。为了处理因为站点已更改而导致用户想要建立另一个快照的情况,我们应该在提要被编辑时删除保存的副本。代码片段展示了这是如何以两步过程完成的:按名称检索资源,然后在返回的对象上调用 deleteResource 方法。


清单 9. saveFeedEntry 方法
try {
    entry.generateURL(rdata.getBaseUrl(), this.getId() );
    entry.addAttribute(Html2XmlUrlViewBean.PARAM_HTMLURL, sHtmlUrl , this.getId() );
    entry.addAttribute(Html2XmlUrlViewBean.PARAM_SNAPSHOT , snapshot , this.getId() );
    // after every edit, cleanup any previously cached snapshot
    Resource  oldRes = entry.getResource( Html2XmlConstants.CACHED_XHTML );
    if ( oldRes != null )
        oldRes.deleteResource();
    } catch (HubException ex) {
        log.error("Error adding entry attribute.",ex);
}

我们终于介绍完了编辑器,下面就来介绍生成器。





回页首


GeneratorPlugin

Html2XmlGeneratorPlugin 类扩展 BaseGeneratorPlugin,并且必须实现抽象方法 generateFeed。不足为奇的是,类型为 RequestData 和 Entry 的输入参数跟传递到提要生成框架调用的 EditorPlugin 方法的参数相同。为了生成提要,必须首先检索包含在编辑过程中保存的配置信息的属性。这通过从 Entry 调用 getAttribute 方法做到。


清单 10. generateFeed 方法
				public FeedContent generateFeed(RequestData rdata, Entry entry) {
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error:  The previous line is longer than the max of 90 characters ---------|
    String  sHtmlUrl  = entry.getAttribute(Html2XmlUrlViewBean.PARAM_HTMLURL );
    String  snapshot  = entry.getAttribute(Html2XmlUrlViewBean.PARAM_SNAPSHOT );

由于该插件没有参数化支持,所以我们不需要检索运行时提供的参数。我们要么返回保存的 XML 内容,要么检索 HTML 内容并使用 JTidy 转换成 XML。逻辑演示了资源是如何创建的,并且非常直观。


清单 11. generateFeed 体
String result = "Html might have changed.  Table not found.";

Resource  oldRes = entry.getResource( Html2XmlConstants.CACHED_XHTML );
if ( "y".equals( snapshot ) &&  oldRes != null  ) {
   log.warn( "returning cached, snapshot=" + snapshot );
   return
				new FeedContent(oldRes.loadResource(), entry.getLifeTime());
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error:  The previous line is longer than the max of 90 characters ---------|
}
            
String sHtml = getXhtml( sHtmlUrl );
if ( sHtml.length() > 0 ) {
    result = sHtml;
    if ( "y".equals( snapshot ) ) {
        try {
            Resource prepared = new Resource();
            prepared.setObjectid( entry.getObjectId() );
            prepared.setMimetype( "text/xml; charset=utf-8" );
            prepared.setFilename( Html2XmlConstants.CACHED_XHTML );
            prepared.uploadResource( sHtml.getBytes( "utf-8" ) );
       } catch (HubException e) {
            log.error(e);
       }
   }
}
return
				new FeedContent( result.getBytes( "utf-8" ), entry.getLifeTime());
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error:  The previous line is longer than the max of 90 characters ---------|

有关 HTML 输入如何被转换成 XML 更为详细的内容可在 java 源文件中找到。我只提及两个关键要点。为了让输出 XML 可被 feed mashup editor 使用,我们抽出了所有 DOCTYPE 声明。此外,生成逻辑做了简单的假设,即输入 HTML 是 UTF-8 格式,需要增强以支持其他语言。





回页首


部署

完整的 Eclipse 项目及所有源文件在下载区域中作为 zip 文件可用。此外,为了让尝试该插件更为容易,也提供了插件 zip 文件(sample.mashupcenter.tidyhtml.zip)。要安装插件,请执行以下步骤:

  1. 从参考资料小节中的链接下载 Tidy.jar。
  2. 从包 org.w3c.dom 删除类文件之后,将 Tidy.jar 添加到插件 zip 文件中的目录 WEB-INF/lib 下。
  3. 将插件 zip 文件放置在 <WebApplication>/WEB-INF/plugins 目录中。
  4. 停止并重启服务器。




回页首


结束语

我们已经介绍了一个更为复杂的插件的构造,涉及到多个编辑器页面、自定义 JavaScript 和资源的保存。您现在已经打下基础,可以开始扩展 IBM Mashup Center 的提要生成功能了。一篇后续文章将讨论更为高级的主题,比如安全性和参数化。






回页首


下载

描述名字大小下载方法
Samples for this articleDownload.zip325KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术

讨论


关于作者

Louis Mau 是 InfoSphere MashupHub 开发团队中的一员。他目前的主要任务是帮助客户使用 IBM Mashup Center 构建情景应用程序。在此之前,他是 DB2 Everyplace Sync Server 的架构师。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款