跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

使用 XPages Extensibility API 创建和部署自定义控件

林 云志, 高级软件工程师, IBM
林云志,目前从事 Lotus Expeditor 的开发工作,对 Eclipse 插件开发和 XPages 开发有丰富的经验。

简介: XPages 是目前 Notes/Domino 开发中非常热门的一项技术,也是 Notes/Domino 未来技术发展的重要方向。Extensibility API 则是 XPages 新提供的一项强大功能。通过使用 Extensibility API,开发人员可以对 XPages 进行扩展。本文就将向您介绍如何使用这套 API 进行开发。

发布日期: 2011 年 11 月 17 日
级别: 初级
访问情况 : 1057 次浏览
评论: 


XPages Extensibility API 简介

XPages Extensibility API 是 Notes/Domino 8.5.2 的一个新功能。它是一套 Java 的 API。通过使用这一套 API,开发人员可以访问 XPages 运行时的信息,并且可以对 XPages 的组件库进行扩展,创建自定义的组件。自定义的组件可以被打包成扩展组件库 (Extension Library),集中部署到 Notes 客户端或者 Domino 服务器上。Extensibility API 是 XPages 开发的一个进阶知识,在开始使用 Extensibility API 之前,希望您对 XPages 的基本开发已经有一定的了解。

我们知道,XPages 是基于 JSF 框架的。对 JSF 有一定了解的开发人员,可以很容易的从 XPages 的开发中看到很多 JSF 的概念,如 Converter, Validator, Managed bean 等等。JSF 的相关概念在 XPages 中基本都是适用的, 开发人员也可以在 XPages 的开发中,使用 JSF 的 API。另一方面,JSF 是一个开放的框架,它允许开发人员开发自定义的扩展。这里说的扩展不仅局限于 UI 控件,也包括非 UI 控件,如 Converter 等。 在 Notes/Domino 8.5.2 之前, 这部分扩展 API 并没有公开给第三方的开发人员使用。为了满足 XPages 开发中的高级需求,8.5.2 发布了 Extensibility API,同时 Domino Designer 对 Extensibility API 的开发也提供了良好的支持。IBM 还基于 Extensibility API, 创建了一个 XPages Extension Library。Extension Library 提供了很多有用的 UI 控件,如对话框,菜单,工具条,还提供了 REST 服务相关的功能。我们可以从 OpenNTF 站点上下载 Extension Library,它的源代码也可以通过 OpenNTF 获得。在使用 Extensibility API 进行开发的时候,Extension Library 的源代码是一个非常好的参考。目前,我们可以自定义的组件包括:

  • UI 控件
  • Converter
  • Validator
  • 数据源
  • Language binding
  • Simple Actions

大家可能会问,XPages 已经提供了 Custom Control 的方式定义自定义组件,为什么还要提供 Extensibility API ?这两种方式又有什么区别?就我的理解,它们的区别在于:

  1. Custom Control 主要是通过组合已有控件的方式进行扩展,大多数情况下,它主要是用来定义 UI 控件。而 Extensibility API 不仅仅可以扩展 UI 控件,还可以扩展非 UI 控件。此外,Extensibility API 不局限于对已有的控件进行组合,开发人员可以定义全新的控件。
  2. Extensibility API 提供了丰富的 API 让开发人员可以访问 XPages 运行时的信息,在 Extensibility API 发布之前,Custom Control 中能访问的信息相对较少。。
  3. Custom Control 的门槛较低,比较容易上手和使用。而 Extensibility API 较为复杂,需要对 XPages 有更深入的了解。如果在使用 Custom Control 就可以满足使用需求的情况下,没有必要一定要使用 Extensibility API。
  4. 在组件重用方面,Custom Control 的代码是包括在 nsf 中的,重用的时候需要在 nsf 中进行拷贝;使用 Extensibility API 创建的组件可以被打包成 library 的方式,方便的进行部署和共享。

下面我们就来看使用 Extensibility API 创建自定义组件的步骤。


创建自定义的控件

我们首先在 Designer 中创建一个新的应用程序。在这里我们将示例程序命名为 ExtSample, 选择 Blank 模板。


图 1. 新建应用程序
图 1. 新建应用程序

前面说过,Extensibility API 是一套 Java 的 API,在开发的时候主要使用的是 Java。新版的 Domino Designer 是构架在 Eclipse 平台之上的,因此,开发人员在开发的时候可以很方便的使用 Eclipse 强大的 Java 编辑功能。我们可以通过菜单栏打开熟悉的 Java 开发视图,选择 Window -> Open perspective -> Java。


图 2. Java 界面
图 2. Java 界面

接下来我们要创建一个 Java 源文件目录,用于存放我们的 Java 代码。从图 2 中可以看到,新建的应用中已经包括了一个名为 Local 的 Java 源文件目录,注意不要在这个目录下创建我们自定义的 Java 代码。这个 Local 目录并不是保存在 nsf 文件中的。当我们将开发好的 nsf 程序拷贝到别的机器上的时候,这个目录下的 Java 代码会丢失。我们需要创建一个另外的源文件目录。通常我们可以使用 WebContent/WEB-INF/src。

下面我们可以开始创建自定义的组件。 所有的 UI 控件都必须从 javax.faces.component.UIComponent 类继承,为了实现的方便,我们可以继承自 UIComponentBase,它为一些方法提供了默认的实现。 UIComponent 描述了一个 UI 控件的状态以及行为,它可以自己负责 UI 的绘制工作,但通常都会将 UI 绘制代理给一个 Renderer 对象来进行。这和 JSF 早期的一个设计目标有关。JSF 希望同样的一个 UIComponent,在搭配不同的 Renderer 的情况下,可以展示出不同的 UI。这样,在使用不同的设备(浏览器,手机)来访问同一个页面的时候,JSF 可以根据设备的不同,选择合适的 renderer,从而绘制出相应的页面。这一步我们暂时不需要加入额外的代码,实现如下:


清单 1. 新建控件类
				
 public class SampleComponent extends UIComponentBase { 
  @Override 
  public String getFamily() { 
    return null; 
  } 
 } 

为了能够让 Designer 识别出我们新定义的组件,并且可以在 XPages 中使用,我们需要进行注册。注册的方式是通过 xsp-config 文件。xsp-config 文件只在开发过程中被 Designer 识别,在 XPages 运行时是不起作用的。这个文件的主要作用是向 Designer 提供控件的各种信息。一个 xsp-config 文件中可以注册多个组件。我们在 WebContent/WEB-INF 目录下创建一个 sample.xsp-config 文件,内容如下:


清单 2. 注册控件信息
				
 <faces-config> 
  <faces-config-extension> 
    <namespace-uri>http://www.ibm.com/xsp/sample</namespace-uri> 
    <default-prefix>sa</default-prefix> 
  </faces-config-extension> 

  <component> 
    <description>This is a sample control.</description> 
    <display-name>Sample Control</display-name> 
    <component-type>com.ibm.sample.SampleComponent 
    </component-type> 
    <component-class>com.ibm.sample.SampleComponent 
    </component-class> 
    <component-extension> 
      <component-family>com.ibm.sample.SampleComponent 
      </component-family> 
      <renderer-type>com.ibm.sample.SampleComponent 
      </renderer-type> 
      <tag-name>sample</tag-name> 
    </component-extension> 
  </component> 
 </faces-config> 

在这个文件中,最重要的几个信息是控件的 namespace、class 以及 tag。Domino Designer 会根据这三个属性,匹配 XPages 页面的标签和 Java 类。

我们可以新建一个 XPages 页面来测试一下我们的自定义组件是否已经被 Designer 所识别。打开新建的 XPages 页面后,在控件面板中点击 Other,在弹出的对话框中选择 Other Controls。如果之前的步骤正确的话,我们就可以看到新创建的组件,如图 3 所示。我们在 xsp-config 文件中定义的属性会被 Designer 显示在 Control details 中。如果在控件面板中看不到自定义的控件,则说明 xsp-config 文件中存在错误。我们可以查看 Designer 的 log,来了解和分析错误信息。可以通过选择菜单栏的选择 Help -> Support -> View Trace 打开 log 显示。


图 3. 在控件面板中选择自定义的组件
图 3. 在控件面板中选择自定义的组件

接下来我们就开始创建 Renderer。Renderer 需要在 faces-config.xml 文件中定义,定义内容如下:


清单 3. 定义 renderer
				
 <?xml version="1.0" encoding="UTF-8"?> 
 <faces-config> 
  <render-kit> 
    <renderer> 
      <component-family>com.ibm.sample.SampleComponent</component-family> 
      <renderer-type>com.ibm.sample.SampleComponent</renderer-type> 
      <renderer-class>com.ibm.sample.SampleRenderer</renderer-class> 
    </renderer> 
  </render-kit> 
 </faces-config> 

XPages 根据 UIComponent 的 component-family 和 renderer-type 来确定对应的 Renderer。因此我们就需要修改前面定义的 SampleComponent 类,覆盖 getFamily 和 getRendererType 方法,返回对应的值。细心的读者可能会问,xsp-config 文件中已经声明了 component-family 和 renderer-type,为什么我们在 Java 代码中还要重复这一信息?这是因为 xsp-config 文件只在开发时起作用,而 Renderer 的匹配则是在运行时决定的。为了在运行时也能获得相应的信息,我们就需要通过这两个方法来实现。


清单 4. 修改控件类,返回 renderer 所需信息
				
  @Override 
  public String getFamily() { 
    return "com.ibm.sample.SampleComponent"; 
  } 
  
  @Override 
  public String getRendererType() { 
    return "com.ibm.sample.SampleComponent"; 
  } 

Renderer 类需要继承 javax.faces.render.Renderer。这里我们覆盖 encodeEnd 方法,在这个方法里面生成 HTML 的输出,代码如下:


清单 5. SampleRenderer 的实现
				
 public class SampleRenderer extends Renderer { 
  @Override 
  public void encodeEnd(FacesContext context, UIComponent component) 
 throws IOException { 
    
    ResponseWriter writer = context.getResponseWriter(); 
    writer.startElement("div", component); 
    Map attr = component.getAttributes(); 
    writer.writeText("Hello World. ", null); 
    writer.endElement("div"); 
  } 
 } 


添加控件属性

通常我们都会需要给控件添加一些自定义的属性。在页面开发人员使用这些控件的时候,可以通过自定义属性,对控件做一定程度的配置。例如 XPages 自带的大多数控件都提供了 style 和 styleClass 这两个属性,用于指定 css。接下来我们就将给自定义控件添加这两个属性。

首先我们要修改 SampleComponent 类,加入相应的成员变量,以及 getter/setter 方法,修改如下:


清单 6. 定义控件属性以及 getter/setter 的实现
				
  private String style; 
  private String styleClass; 
  
  public String getStyle() { 
    if(style != null) 
      return style; 
    
    ValueBinding vb = getValueBinding("style"); 
    if(vb != null) 
      return (String)vb.getValue(getFacesContext()); 
    else 
      return null; 
  } 

  public void setStyle(String style) { 
    this.style = style; 
  } 

  public String getStyleClass() { 
    if(styleClass != null) 
      return styleClass; 
    
    ValueBinding vb = getValueBinding("styleClass"); 
    if(vb != null) 
      return (String)vb.getValue(getFacesContext()); 
    else 
      return null; 
  } 

  public void setStyleClass(String styleClass) { 
    this.styleClass = styleClass; 
  } 

值得注意的是 get 方法,在 XPages 的开发中,我们可以通过服务器端的 Javascript(SSJS) 或者值绑定的方式动态的给属性赋值,如果这个属性允许用户通过 SSJS 或值绑定的方式赋值的话,我们就需要在相应的 get 方法中通过 JSF 的 API 对其进行解析。

此外,我们还要实现 saveState 和 restoreState 方法,XPages 在保存和恢复页面的时候通过这两个方法进行状态的保存和恢复,它们的实现必须保证对应关系,这样保存的状态才能够正确的被恢复。saveState 和 restoreState 可以按照以下的方式来实现:


清单 7. saveState 和 restoreState 的参考实现
				
  @Override 
  public void restoreState(FacesContext context, Object state) { 
    Object[] values = (Object[])state; 
    super.restoreState(context, values[0]); 
    style = (String)values[1]; 
    styleClass = (String)values[2]; 
  } 

  @Override 
  public Object saveState(FacesContext context) { 
    Object[] values = new Object[3]; 
    values[0] = super.saveState(context); 
    values[1] = style; 
    values[2] = styleClass; 
    return values; 
  } 

为了使得 Designer 可以正确的识别新定义的属性,我们需要修改 xsp-config 文件。在 xsp-config 文件中,我们可以定义属性在 Designer 中显示的时候所属的分类,以及编辑这个属性时候所用的编辑器。Designer 内建了一些不同种类的编辑器可以方便我们编辑属性值,这里我们选用最简单的字符串编辑器。xsp-config 文件新增的内容如下:


清单 8. 向 Designer 注册自定义属性
				
    <property> 
      <description>CSS Styles</description> 
      <display-name>style</display-name> 
      <property-name>style</property-name> 
      <property-class>java.lang.String</property-class> 
      <property-extension> 
        <Designer-extension> 
          <category>styling</category> 
          <editor>com.ibm.std.String</editor> 
        </Designer-extension> 
      </property-extension> 
    </property> 

    <property> 
      <description>CSS Style Class</description> 
      <display-name>styleClass</display-name> 
      <property-name>styleClass</property-name> 
      <property-class>java.lang.String</property-class> 
      <property-extension> 
        <Designer-extension> 
          <category>styling</category> 
          <editor>com.ibm.std.String</editor> 
        </Designer-extension> 
      </property-extension> 
    </property> 

修改 xsp-config 文件之后,我们可以在 XPages 页面中测试一下结果。如果 Java 文件和 xsp-config 文件中的声明都正确的话,我们就可以在 Designer 的属性面板中看到新定义的属性,如图 4 所示。


图 4. 自定义属性
图 4. 自定义属性

最后我们需要修改 Renderer 来使用新定义的属性。


清单 9. 在 renderer 中读取属性值并生成输出
				
  String style = (String)attr.get("style"); 
  String styleClass = (String)attr.get("styleClass"); 
  if(style != null && style.trim().length() != 0) 
    writer.writeAttribute("style", style, null); 
    
  if(styleClass != null && styleClass.trim().length() != 0) 
    writer.writeAttribute("class", styleClass, null);  

我们可以创建一个 XPages 页面来测试一下最后的结果。页面的内容如下:


清单 10. 测试页面
				
 <?xml version="1.0" encoding="UTF-8"?> 
 <xp:view xmlns:xp="http://www.ibm.com/xsp/core"
  xmlns:sa="http://www.ibm.com/xsp/sample"> 
  <sa:sample id="sample1" style="color:blue"> 
  </sa:sample> 
 </xp:view> 

生成的显示如图 5 所示。


图 5. XPages 显示
图 5. XPages 显示

至此,我们已经完成了使用 Extensibility API 创建自定义 UI 控件的所有步骤。


创建 Extension Library

定义好的自定义控件需要能够方便的部署到页面开发人员的 Designer 中,以及 Domino 生产服务器上。XPages 支持将自定义的控件打包成组件库来进行部署。一个组件库实际上就是一个 Eclipse 插件,包括了自定义控件的 Java 代码和相关的 xml 文件。组件库的工程结构如图 6 所示。


图 6. Library 的工程结构
图 6. Library 的工程结构

从图 6 中可以看到,组件库的很多代码在前面的步骤中都已经完成了,我们所需要做的只是将代码从 nsf 文件中复制到独立的插件工程里。有一点要特别注意的小区别是,我们将 faces-config.xml 文件重命名为了 sample-faces-config.xml 文件。这是因为每个应用中只能存在一个 faces-config.xml 文件,而 XPages 应用的 nsf 中已经包括一个 faces-config.xml 文件了,所以这里就不能再使用这个文件名。

唯一需要实现的是 SampleLibrary 类。它用于描述这个组件库中包括了哪些组件以及相关的信息。首先我们需要向 XPages 注册一下自定义的组件库,XPages 提供了一个扩展点来进行注册,定义的方式如下


清单 11. 定义 library 扩展
				
   <extension 
         point="com.ibm.commons.Extension"> 
      <service 
            class="com.ibm.sample.library.SampleLibrary"
            type="com.ibm.xsp.Library"> 
      </service> 
   </extension> 

SampleLibrary 需要继承自 AbstractXspLibrary 类,实现如下:


清单 12. SampleLibrary 的实现
				
 public class SampleLibrary extends AbstractXspLibrary { 

  public SampleLibrary() { 
    
  } 

  @Override 
  public String getLibraryId() { 
    return "com.ibm.sample.library"; 
  } 

  @Override 
  public String[] getFacesConfigFiles() { 
    return new String[]{"META-INF/sample-faces-config.xml"}; 
  } 

  @Override 
  public String getPluginId() { 
    return "com.ibm.sample.library"; 
  } 

  @Override 
  public String[] getXspConfigFiles() { 
    return new String[]{"META-INF/sample.xsp-config"}; 
  } 

 } 

作为一个 Eclipse 插件,扩展组件库在 Notes/Domino 中的部署方式是和一般的插件部署是相同的。这里就不再赘述。大家可以参考 OpenNTF 上 XPages Extension Library 文档的部署部分。

安装完成以后我们可以新建一个 XPages 应用检测一下是否可以使用新安装的控件了。要注意的是,这里控件不再默认出现在 Other 分类下,而是出现在 Extended 分类下面。


图 7. 部署的控件在控件面板中的位置
图 7. 部署的控件在控件面板中的位置

总结

希望大家在看了这篇文章以后,对如何使用 Extensibility API 创建自定义控件有更好的了解。除了创建 UI 控件以外,我们还可以创建非 UI 控件。即使我们没有创建自定义控件的需求,里面的很多 API 在我们写服务器端逻辑的时候,也是很有帮助的。



下载

描述名字大小下载方法
样例代码com.ibm.sample.library_1.0.0.jar8KBHTTP
样例代码ExtSample.nsf384KBHTTP

关于下载方法的信息


参考资料

学习

讨论

关于作者

林云志,目前从事 Lotus Expeditor 的开发工作,对 Eclipse 插件开发和 XPages 开发有丰富的经验。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Lotus
ArticleID=775496
ArticleTitle=使用 XPages Extensibility API 创建和部署自定义控件
publish-date=11172011

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。