借助 Project Zero 在 Web 2.0 领域创建 RESTful IBM Lotus Domino 应用程序

用 Project Zero 为 Lotus Domino 提供 REST 支持

大多数 Lotus Domino 开发人员都熟悉采用各种语言(LotusScript. 最为著名)编写的 Lotus Domino 代理。基本上,RESTful 应用程序(具有之前介绍的 REST 原则)都可通过 Web 访问。此外,可以通过命名资源对应用程序寻址。使用 LotusScript. 或简单代理等约定来实例化应用程序脱离了 REST 的基础,即命名资源。

Lotus Domino 代理需使用类似于 http://domino.ibm.com/database.nsf/AgentName?OpenAgent 的 URL 通过 Web 调用,如图 1 所示。这绝非我们希望实现的命名资源,并且任何信息都可作为参数传递。这种实现或等价的 servlet 实现与基于动词的实现同义,而不同与 RESTful 特性。举例来说,代理或 servlet 可以接受 ?action=create&user=vstaub 这样的参数;而另一方面,RESTful 方式只操作 POST 方法中的数据。


图 1. Lotus Domino 代理的命名资源
Lotus Domino 代理的命名资源

Lotus Domino Security API (DSAPI) 过滤器可能是一个比较合适的实现。DSAPI 过滤器位于 Lotus Domino HTTP 栈,并且可以在满足 URL 中的条件时调用。熟悉 IBM Lotus Quickr 人的都知道,所有 URL containing /quickr 都由 Lotus Quickr 服务器的 DSAPI 过滤器处理。使用 DSAPI 过滤器,实现将有效地嵌入到 Lotus Domino 服务器的 HTTP 栈中。遗憾的是,创建 DSAPI 对许多人来说都是一项令人畏惧的任务,因为它要求同时掌握 DSAPI 工具包和 C 编程语言。如果 RESTful 应用程序旨在促进 /database/document_unid 或 /database/view_name 命名资源约定,那么它可以与已有 Lotus Domino Web 服务器无缝操作,从而在预先定义的条件下调用 RESTful 操作。请参见图 2。根据读者的背景,此实现可能过于技术化或费时。


图 2. Lotus Domino DSAPI 过滤器的命名资源
Lotus Domino DSAPI 过滤器的命名资源

最后,考虑 Java 语言。自 Lotus Domino 6 以来,我们能够使用 Java 访问 Lotus Domino 资源,如数据库、视图或支持 Java 的应用程序中的文档。在 Java 访问中结合 servlet 便利 Web 请求,并且您可通过解决方案在互联网上与 Lotus Domino 资源交互。但如前所述,servlet 实现并非完全是 RESTful 的。通过使用日益流行的 Project Zero 项目,应用程序的支持 Web 的 RESTful 功能得到了极大的改进。


图 3. Project Zero 应用程序的命名资源
Project Zero 应用程序的命名资源 

识别资源

在 Lotus Domino Directory 中,我们可以识别以下资源:

  • Employee:个人记录(等价于 Lotus Notes 文档)。
  • Employees:一组员工(等价于一个 Lotus Notes 文档或一个 Lotus Notes 视图)。

识别资源的另一个重要方面是理解它的寻址方式。作为默认端点的的一部分,员工是可访问的:http://[hostname]/employees。

可以使用简短名称或电子邮件等惟一标识符来访问员工:http://[hostname]/employees/[uid]。

资源表示

Lotus Domino Employees 应用程序和客户机或浏览器之间的数据交换是使用 JavaScript. Object Notation (JSON) 完成的。使用 JSON 的原因在于已有的 Project Zero 支持以及它在 JavaScript. 应用程序中提供的轻量级且易于使用的数据格式。基本上,JSON 用于表示数据,并且 Lotus Domino Directory 中的用户可以作为 JSON 对象出现,如清单 1 所示。


清单 1. 员工的 JSON 表示

{
      "FirstName": "Van",
      "FullName": "CN=Van Staub\/O=IBM",
      "HTTPPassword": "(355E98E7C7B59BD810ED845AD0FD2FC4)",
      "InternetAddress": "vstaub_cnnew1@ibm.com",
      "LastName": "Staub",
      "OfficeCity": "Atlanta",
      "OfficePhoneNumber": "(123) 456-7890",
      "OfficeState": "Ga",
      "OfficeStreetAddress": "4111 Northside Pkwy",
      "ShortName": "vstaub"
}

本文将在稍后介绍,在 JavaScript. 客户机中使用该数据的能力是即时的。JavaScript. 客户机应用程序将 JSON 作为对象接收,因此需使用点表示(dot notation)语法引用它的值。举例来说,要访问用户的姓,需要使用语法 object.LastName。此语法将降低任何解析需求,并便利对数据的直接访问。有关 JSON 的更多信息,请参阅 JSON 网站

映射方法

表 1 演示了用于实现各用例的 HTTP 方法。


表 1. 用例 HTTP 方法类型

用例资源HTTP 方法
查看员工员工GET
查看员工员工GET
创建员工员工POST
更新员工员工PUT
删除员工员工DELETE

响应代码

表 2 详细给出了操作的 HTTP 响应以及错误情况下的服务响应。针对 GET、POST、PUT 和 DELETE 的任何成功请求都将获得一个 200 OK 响应,但错误条件将触发相应的 HTTP 响应。


表 2. 用例 HTTP 代码

用例 失败
查看员工401(未授权)
404(无法找到员工)
查看员工401(未授权)
404(无法找到 UID)
创建员工401(未授权)
303(指定 UID 已存在)
更新员工401(未授权)
403(禁止)
删除员工401(未授权)
403(禁止)

使用 Project Zero 设计

Project Zero 允许开发人员快速创建支持 Web 的应用程序,从而可以专注于 REST。简言之,只需结合少许 Java 编程和 Project Zero,您便可创建和部署 RESTful Lotus Domino 应用程序或服务。Lotus Domino Employees 应用程序使用已有的 Project Zero Employees 演示应用程序解决许多客户端逻辑。这允许读者实现 Project Zero 的多功能性,并且能结合新的特定于 Lotus Domino 的服务端逻辑。

Project Zero 让您能够专注于应用程序的业务逻辑,而不用为处理传入 Web 请求、解析 URL 和处理各种方法大费周折。可以通过两种方法实现 Project Zero 应用程序:PHP 和 Groovy。Groovy 实现巧妙地补充了使用 Java 访问 Lotus Domino 资源的功能。从本质上说,通过在 Groovy 代码中使用 Java,或调用已有 Java 库中的函数,我们可以编写直接访问 Lotus Domino 资源的 Groovy 应用程序。选择在 Groovy 实现,我们可以利用一款 Eclipse 插件来加速应用程序的创建和测试。再说一次,这允许您轻松地完成创建、部署和测试流程。

RESTful Lotus Domino 应用程序和大多数 Project Zero 应用程序的核心都是 RESTful 设计。Project Zero 允许您创建 Groovy 文件(它是一个处理程序)来提供命名资源。然后,客户机可以传递一个虚拟命名资源给处理程序。举例来说,您创建了一个名称为 employees.groovy 的处理程序脚本。然后,用户可通过 URL 访问命名资源 /employees。然后,针对 /employees 的请求可以将目录的整个内容提供给客户机。/employees/username 就是一个类似的 URL 请求。用户名成员是一个虚拟资源。employees.groovy 允许对用户名的值进行访问,而实现又恰当给出了句柄的详细信息。请注意虚拟命名资源和 Groovy 处理程序的文件名称之间的关系。处理程序的实现细节模拟了 REST 架???。创建、更新、检索和删除(CRUD)等操作通过 Groovy 处理程序中的相应函数得以简化,如表 3 所示。


表 3. 定义 Groovy 处理程序

方法类型URLGroovy 处理程序函数
GET /employeesonList()
POST /employeesonCreate()
GET /employees/[uid]onRetrieve()
PUT /employees/[uid]onUpdate()
DELETE /employees/[uid]onDelete()

虽然我们所讨论的多数信息都专注于服务器端,但 Project Zero 也简化了客户端应用程序的创建。流行的 Ajax 框架 Dojo 封装在一个增件库中。因此,您可以创建传入服务请求和支持 Ajax 的富 Web 应用程序。

如果决定使用 Project Zero,那么您将体验激动人心的一流的开发应用程序和架构,并能将 Eclipse 中的一切内容整合到更加开放的 Web 2.0 中。

此外,Project Zero 支持将对象隐式转换为 JavaScript. 表示,即 JSON。我们可以将 Java 对象作为响应轻易提供给客户机,然后该对象将自动转换为 JSON 表示。我们可以轻易利用 Project Zero 平台的便利功能。

Lotus Domino Employees 应用程序由分层的实现策略组成:一个客户机应用程序、Project Zero 应用程序、一组 Java 类和 Lotus Domino 服务器本身,如图 4 所示。


图 4. Lotus Domino 员工应用程序模型
Lotus Domino 员工应用程序模型

客户机(支持 Web 的 JavaScript. 应用程序或自定义服务)将连接到 Project Zero 应用程序。Project Zero 负责与客户机的任何交互,其任务涉及处理传入请求、查找命名资源和发送适当响应。Project Zero 消除了处理传入 Web 请求所需的开销。通过使用内置的 Project Zero 函数来处理 CRUD 操作,我们不必与往常一样在 Web 服务器应用程序中编写大量代码。

Java 类负责与 Lotus Domino 服务器交互。从本质上说,Java 类就是 Lotus Domino Employees 应用程序的逻辑,其实就是一组帮助函数。这些帮助函数可以利用 Lotus Domino Java API 和 notes.jar 文件与存储在 Lotus Domino Directory 中的数据进行交互。

您可以毫不费力地编写代码让 Groovy 类与 Lotus Domino 交互,但我们推荐将应用程序逻辑作为帮助函数分离到 Java 类中。由于 Java 类与 Project Zero 是松散耦合的,因此可以在其他场景中重用应用程序 —— 比如说富客户机或 servlet 中。同样,使用 Java 编写的访问 Lotus Domino 服务器的应用程序可以轻易地整合到 Project Zero 平台中,从而提供支持 Web 的交付。

应用程序行为

我们使用之前介绍的策略来考虑任何 Lotus Domino 应用程序的操作流。假设某客户机操作向 Project Zero 应用程序发起一个异步调用。Project Zero 仅能接收请求和调用必要的 Java 代码来促进所需的操作。Java 类与 Lotus Domino 服务器上的数据交互。然后,可以将 Java 对象序列化并发回请求者。

Lotus Domino Employees 应用程序由一些非常简单的交互构成,即根据对用户的传入操作与 Lotus Domino 进行交互:

  1. 检索 Person 文档中的信息。
  2. 删除 Person 文档。
  3. 注册新用户。
  4. 更新 Person 文档。

表 4 详细说明了 Lotus Domino 服务器的行为。


表 4. 定义 Lotus Domino 行为

方法类型URLGroovy 处理程序函数Lotus Domino 行为
GET/employeesonList()检索 Person 文档中的信息
POST /employeesonCreate() 注册新用户
GET /employees /[uid]onRetrieve()检索 Person 文档中的信息
PUT/employees/[uid]onUpdate()更新 Person 文档
DELETE /employees/[uid]onDelete() 删除 Person 文档

利用 Lotus Domino 安全性

为解决安全问题,我们将使用已有的 Domino 机制:多服务器单点登录(SSO)。Web 用户成功通过 Lotus Domino 的身份验证之后,Lotus Domino 服务器将以 cookie 的形式发起一个加密令牌给浏览器。cookie 中的值可用于身份验证目的。在这种方式下,我们重复使用 cookie 中的加密令牌连接 Project Zero 应用程序中的 Lotus Domino。这对于保护到 Project Zero Web 应用程序的访问具有双重效果:

  • 它确保只有有效用户才可以使用应用程序(身份验证)。
  • 它确保用户访问 Lotus Domino 时只可获得自己权限范围内的内容(授权)。

Project Zero 最近提供了对新的 Lotus Domino LTPAToken 的支持。通过结合 cookie 中的 LTPAToken 值,我们可以根据用户的身份来创建 Lotus Notes 会话。与 Lotus Domino 的所有交互都将从 Lotus Notes 会话的创建开始。

要利用 LTPAToken,管理员可以将服务器配置为单点登录。可以在 IBM Lotus Domino Administrator 或 Lotus Notes 客户机中完成此操作,方法是??建一个 Web SSO Configuration 文档,如图 5 所示。


图 5. 创建 Lotus Domino Web SSO Configuration 文档
创建 Lotus Domino Web SSO Configuration 文档

接下来,配置 Lotus Domino 服务器在 Server 文档中使用 Web SSO Configuration 文档,方法是使用刚才创建的 Web SSO 配置将 Session 身份验证方法更新为 Mutliple Servers (SSO)。参见图 6。


图 6. 为 Lotus Domino 配置 SSO
为 Lotus Domino 配置 SSO

注意,Lotus Domino 生成的 LTPAToken 仅对于 Web SSO Configuration 文档中列出的 DNS 域有效。此外,LTPAToken 只传递给使用 Web SSO Configuration 文档中的域访问的服务器。从本质上说,这表示您希望使用与访问 Lotus Domino 相同的 URL 来访问 Lotus Domino Employees Web 应用程序。不要使用本地主机名或 IP 地址。

要获取 LTPAToken,登录到 Lotus Domino 服务器,如图 7 所示。


图 7. 在浏览器中创建 LTPAToken
在浏览器中创建 LTPAToken

在浏览器的地址栏输入以下消息,验证您的 LTPAToken:

javascript.:alert(document.cookie)

请参见图 8。


图 8. 确认您拥有 LTPAToken
确认您拥有 LTPAToken

使用 Lotus Notes 会话对象

使用 Java 类与 Lotus Domino 的所有交互都源自会话对象。通过会话对象,程序员可以发起各种强大的操作,如打开数据库、发送控制台命令、检索 Notes.ini 名称/值对和注册新用户。在 Lotus Domino Employees 应用程序中,会话对象使用从浏览器传递到应用程序的 LTPAToken 构建。这种流程允许应用程序只执行登录用户权限范围内的操作。

获取 Lotus Notes 会话对象的详细信息也是值得一提的。根据开发环境以及所实现应用程序的目的,对象的实现形式也有所不同。Lotus Domino 设计者编程指南,第 3 卷 Java CORBA 类 概述了获取 Lotus Notes 会话对象之间的差异。

可以使用以下两种方式创建 Lotus Notes 会话对象:使用远程过程调用(Remote Procedure Call,RPC)或使用 Internet 对象请求代理间协议(Internet Inter-ORB Protocol,IIOP)。RPC 调用要求将 Lotus Notes 客户机、IBM Lotus Domino Designer 或 Lotus Domino 服务器安装在运行的应用程序的本地。Lotus Domino Employees 应用程序就是这种形式的一个例子,它将在运行中的 Lotus Domino 服务器上创建一个本地会话。这种选择旨在最大限度地提升性能,以提供快速响应的 Web 接口。

为 Project Zero 配置 RPC 会话

要执行任何 Lotus Notes Java 编程,必须在项目的类路径中包含合适的 Lotus Notes Java 库。在使用 RPC 调用时,需在应用程序的 Java 编译路径中包括 notes.jar 文件。第一次运行 Project Zero 应用程序时,可能会出现如图 9 所示的错误消息。


图 9. 缺少 Lotus Notes Java 库
缺少 Lotus Notes Java 库

从本质上说,此错误表示无法找到 nlsxbe 库的本机调用,您必须将它添加到 Project Zero 应用程序的 java.library.path 变量中。notes.jar 库向 Lotus Domino 程序目录中的函数发起本机调用。反过来,本机函数又调用其他本机函数。如果您在 Eclipse 中有过 Lotus Notes Java 编程经验,那么可能知道最简单的解决方案就是将 Lotus Domino 或 Lotus Notes 程序目录添加到系统的 PATH 环境变量中。在 Project Zero 中,需求则稍有不同。要解决之前的错误,将 Lotus Domino 或 Lotus Notes 程序目录的路径添加到 Project Zero 应用程序的运行时配置中,如图 10 和 11 所示。

在 Run 对话框中选择 Arguments 选项卡,如图 10 所示。然后,在 VM 参数窗格中,将 Lotus Domino 或 Lotus Notes 安装的路径附加到 java.library.path 参数中:

-Djava.library.path="${ZERO.NATIVES};C:/Lotus/Domino"

记住使用分号将任何已有参数与最新添加的 Lotus Domino 或 Lotus Notes 安装路径分隔开。


图 10. 配置 Lotus Domino VM 参数
配置 Lotus Domino VM 参数

在 Run 对话框中选择 Environment 选项卡,如图 11 所示。编辑列表中的 PATH 变量,然后在出现的 Edit Environment Variable 对话框中附加 Lotus Domino 或 Lotus Notes 安装的路径。

再说一次,记住使用分号将任何已有参数与最新添加的 Lotus Domino 或 Lotus Notes 安装路径分隔开。


图 11. 配置 Lotus Domino PATH 变量值
配置 Lotus Domino PATH 变量值

创建 RPC 会话

假设用户已经通过了 Lotus Domino 服务器的身份验证,Groovy 文件会将 LTPAToken 传递给 Java SessionHelper 类。

Session session= SessionHelper.getInstance()
.getSession(zget("/request/cookies/in").get("LtpaToken"));

而 SessionHelper 类将创建 Lotus Notes 会话对象,如清单 2 所示。


清单 2. Lotus Notes 会话对象的创建

// verify that the Zero HTTP thread has been properly
// initialized for Notes RPC communication
Long threadId= new Long(NotesThread.currentThread().getId());
		
System.out.println("Zero thread : " + threadId + " using LtpaToken : " + ltpaToken);
		
…
// already created the NotesThread?
if(sessions.containsKey(threadId)){
	// not really an error
	System.err.println("NotesThread already initialized");
} else {
	System.out.println("Initializing NotesThread");
		
	// initialize the NotesThread
	NotesThread.sinitThread();
}
			
// create a Notes Session
// use null to indicate an RPC connection to the
// server on which the Project Zero application runs
Session session= NotesFactory.createSession(null, ltpaToken);
		
System.out.println("Storing NotesSession for Zero thread : " + threadId);
		
// cache the session for future use
sessions.put(threadId, session);
		
System.out.println("NotesSession created for user " + session.getUserName());
		
return session;

…

在发起 RPC 调用时,必须在初始化 NotesThread 对象之后才能利用该会话。此步骤并非 IIOP 实现所必需的。您还必须在处理结束时调用 stermThread 函数,以避免异常终结对运行中的 Lotus Domino 服务器造成影响。获得会话对象之后,可以使用由 LTPAToken 定义的授权给用户的访问控制级别在它与 Lotus Domino 服务器之间实现交互。

大多数时间,我们所投资的??目都涉及到如何处理 Lotus NotesThread 对象。Project Zero 应用程序默认使用三个单独的线程以轮叫的形式处理传入 HTTP 请求。在与 Lotus Domino 交互之前,NotesThread 类必须正确完成各个请求的初始化任务。初始化失败通常将导致清单 3 所示的错误。


清单 3. 未正确初始化 NotesThread 的错误

Caused by: java.lang.UnsatisfiedLinkError: NCreateSessionWithTokenOrName
	at lotus.domino.local.Session.NCreateSessionWithTokenOrName(Native Method)
	at lotus.domino.local.Session.createSessionWithTokenOrName(Unknown Source)
	at lotus.domino.NotesFactory.createSessionC(Unknown Source)
	at lotus.domino.NotesFactory.createSession(Unknown Source)
	at com.ibm.devworks.SessionHelper.createSession(SessionHelper.java:24)
	at com.ibm.devworks.SessionHelper.getSession(SessionHelper.java:60)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod.invoke
	(ReflectionMetaMethod.java:56)
	at org.codehaus.groovy.runtime.MetaClassHelper.doMethodInvoke
	(MetaClassHelper.java:599)
	at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1030)
	at org.codehaus.groovy.runtime.Invoker.invokeMethod(Invoker.java:69)
	at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:66)
	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN
	(ScriptBytecodeAdapter.java:165)
	at employees.onList(employees.groovy:23)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod.invoke
	(ReflectionMetaMethod

UnsatisfiedLinkError 表示 NotesThread 未正确初始化。

完成初始化之后,HTTP 线程必须得到正确处理。NotesThread.stermThread() 函数为线程的处理任务提供了方便。我们的意图是在 Zero 中接收到传入请求时初始化 Zero HTTP 线程,随后处理 NotesThread 并呈现输出。请参见清单 4。


清单 4. 处理会话对象和 NotesThread

def onList() {

    try {
        
        // create a Session from the SessionHelper
        // all Domino operations require a Session
        Session session= SessionHelper.getInstance()
        .getSession(zget("/request/cookies/in").get("LtpaToken"));
        
        …

    } catch (NotesException e) {

        request.status = HttpURLConnection.HTTP_INTERNAL_ERROR
          request.error.message = e.getMessage()
        request.view = "error"

    }
    
    // properly dispose of any Domino objects by using the
    // SessionHelper
    SessionHelper.getInstance().shutdown();
    
    // finally render the content to the client
    render()

}

/**
* Properly dispose of any remaining Sessions or NotesThreads.
*
*/
public void shutdown() {
    
    System.out.println("Closing NotesSession ...");
    
    // get the Zero thread requesting Notes shutdown
    Long threadId= new Long(NotesThread.currentThread().getId());
    
    try {
        Session session= (Session)sessions.get(threadId);
    
        System.out.println("Closing NotesSession for user " + session.getUserName());
        
        session.recycle();
        
        System.out.println("NotesSession closed");
        
    } catch (NotesException e) {
        e.printStackTrace();
    } finally {
        
        // terminate the NotesThread properly
        NotesThread.stermThread();
        
        // remove a the so a NotesThread can be initialized
        // on the Zero thread
        sessions.remove(threadId);
        
        System.out.println("Removed NoteSession for Zero thread : " + threadId);

    }
}

此模型已被证实极具挑战性。有时,当其他请求造成 JVM 崩溃时,NotesThreads 会被正常终止。清单 5 所示的系统崩溃将出现在编程过程中。


清单 5. JVM 崩溃错误内容

#
# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x60001457, pid=2660, tid=6448
#
# Java VM: Java HotSpot(TM) Client VM (1.5.0_12-b04 mixed mode)
# Problematic frame.
# C  [nnotes.dll+0x1457]
#

出现此类错误的原因是对已初始化的 Zero HTTP 线程进行 NotesThread 初始化操作,或尝试终止已初始化 Zero HTTP 线程的 NotesThread 任务。在正确初始化和处理 NotesThreads 以及离开初始化线程之间,我们陷入了两难的境地,但可能会造成 Lotus Domino 服务器的不稳定。基于本文的演示目的,我们选择了后者。可以采取一些相应的措施来解决这种设计问题。您可以扩展 NotesThread 类并将自己的逻辑封装在其中。完成 run() 或 runNotes() 方法后,NotesThread 将被适当处理,但您可能需要实现一些线程来完成非常具体的操作。我们选择的设计方式相当有趣,您可以在 Project Zero 论坛 上发表问题寻求 Project Zero 小组和其他社区用户的帮助。

此处的目的并非阻止您探索 Project Zero 和 Lotus Domino,而是将我们的经验告诉您。本文中所描述的问题可能都有解决方法或已知的技术限制。

还需注意,应用程序将创建一个本地 Notes 会话,但它在访问本地服务器或 Lotus Notes 客户机上的数据时不受任何限制。通过在 getDatabase() 函数中指定 Lotus Domino 服务器名称,您可以更新代码访问远程 Lotus Domino 服务器上的数据库。

有关更多详细信息,请参阅本文稍后的 “使用 Lotus Domino Employees 应用程序” 一节。

创建 IIOP 会话

虽然并未在 Lotus Domino Employees 应用程序中实现,但 IIOP 是 RPC 格式的替代之选。当 Project Zero 应用程序并未运行在拥有本地 Lotus Domino 服务器或 Lotus Notes 客户机安装的服务器中时,IIOP 的作用就会突显出来。放弃本地 Lotus Domino 或 Lotus Notes 安装需求的代价是需要在 Lotus Domino 服务器上运行 DIIOP 任务。DIIOP 任务将促进远程连接访问数据库、视图和文档等内容。

如清单 6 所示,RPC 代码在更新后可便利 IIOP 与 Lotus Domino 的连接。


清单 6. IIOP 连接代码的差异

private static Session createSession(String server, String token) {
System.out.println("Creating Session");

	try {
		// server should be a DNS address or IP		
		Session s = NotesFactory.createSession(server, token);

		System.out.println("Creation successful");

		return s;

	} catch (NotesException e) {
		System.err.println("Creation failed");

		e.printStackTrace();

		return null;
	}
}

注意,惟一的实际变化在于移除了 NotesThread 处理。当您使用 IIOP 时,NotesThread 会话将不再需要。

IIOP 在服务器上引入了新配置考虑:安全性、访问 Lotus Domino 服务器上的 diiop_ior.txt 文件,以及各种端口配置。您还应在确保在 Lotus Domino Server 文档的 Internet Protocols - HTTP 选项卡上选中 “Allow HTTP clients to browse databases” 选项。


图 12. 配置 DIIOP
配置 DIIOP

此设置允许会话获得服务器上的数据库列表??有关 DIIOP 的更多信息,请参阅 Lotus Domino Designer Programming Guide 的 NotesFactory 一节。

访问 Lotus Domino 数据

现在,我们已经使用 Notes 会话建立了 Lotus Domino Employees 应用程序的行为和帮助函数,并开始查询或修改 Lotus Domino 目录。由于 Lotus Domino Employees 应用程序的专注点与用户相关,因此帮助函数将操作 Lotus Domino Person 文档。

举例来说,对 resource /employees 的请求将检索目录中的所有联系方式。此操作等价于获取使用 names.nsf 数据库中的 Person 表单创建的所有文档。清单 7 中的代码演示了此行为。


清单 7. 使用 Lotus Notes 文档集合获取所有员工

public Employees getAllPeople() {
		// conduct a search for people created with the Form. People
		// this is true for all users
		DocumentCollection dc= searchNab("Form=\"Person\"");
		
		// convert the collection into Employees
		Employees contacts= getPeople(dc);
		
		// recycle the document collection
		try {
			dc.recycle();
		} catch (NotesException e) {
			e.printStackTrace();
		}
		
		return contacts;
	}


注意,getAllPeople 函数将发送一个搜索公式给 NAB,以获取所有的 Person 文档,这等同于搜索可以操作 Lotus Domino 数据库的客户机用户。您可以在目录中打开 People 视图并在其中返回 DocumentCollection,而不用打开目录并执行搜索。这种方法允许您更好地利用已有的 Lotus Domino 结构来减少代码和性能开销。它还降低了全文索引目录的需求。

Lotus Domino 数据表示

在之前获取所有员工的检索操作时,所返回的结构是 Notes 对象 DocumentCollection。DocumentCollection 恰如其名;它由一系列 Document 对象组成。Document 对象对应于目录中的实际 Person 文档。为了使数据表示流程清晰可见,我们首先将文档中需要的值复制到新的 Employee 对象中,如清单 8 所示。


清单 8. Document 到 Employee 对象的转换

public Employee(Document doc){
data= new HashMap();
		
	try {
		// verify that the document is a person doc
		if(doc != null &&
			doc.hasItem("Form") &&
			doc.getItemValueString("Form").
				equalsIgnoreCase("Person")){
					
			// pull data as Strings from the person doc
			for(int i= 0; i < Employee.FIELDS.length; i++){
				data.put(Employee.FIELDS[i], doc.getItemValueString(
							Employee.FIELDS[i]));
			}

			notesUrl= doc.getNotesURL();
			
}
	} catch (NotesException e) {
		e.printStackTrace();
		
		notesUrl= "";
	}
}

代码将迭代在 Employee 对象中指定的预先定义的字段名称(如清单 9 所示),并将 Person 文档中相应字段的值复制到 Employee 对象的 HashMap 中。


清单 9. 迭代字段名称

public static final String NAME= "FullName";
public static final String UID= "ShortName";
public static final String FIRSTNAME= "FirstName";
public static final String LASTNAME= "LastName";
public static final String EMAIL= "InternetAddress";
public static final String PHONE= "OfficePhoneNumber";
public static final String PASSWORD= "HTTPPassword";
public static final String ADDRESS= "OfficeStreetAddress";
public static final String CITY= "OfficeCity";
public static final String STATE= "OfficeState";
public static final String ZIP= "OfficeState";

从本质上说,我们在 Employee 对象中模拟了 Document 对象中的名称/值对。在大型系统中,复制实际数据将阻止 Lotus Domino 对象在 JSON 转换流程开始之前被回收或被删除,这有时会造成错误。

将 Lotus Domino 数据转换为 JSON

当 Employee 对象中填充了用户数据,并且需要将它转换为 JSON 并将该 JSON 发送给客户机时,请考虑应用程序的状态。该流程发生在使用请求 employees/[uid] 检索用户时。再一次,Project Zero 极大地简化了这一操作,如清单 10 所示。


清单 10. Project Zero 提供对 JSON 转换的内在支持

// retrieve the Employees Collection by creating a
// NabHelper and requesting all the people
Employees employees= new NabHelper(session).getAllPeople();
				
if(employees != null){
	
// automatically serialize the object to JSON using
	// Project Zero's custom converter
	request.view='JSON'
	request.json.output= employees; 
}

render()

以上代码的关键步骤在于将实际对象作为 JSON 输出提供给客户机。对于简单对象,Project Zero 会使用公有 get 函数自动将对象转换为 JSON。对于比较复杂的对象,您应该使用自定义转换程序定义转换流程。

使用自定义转换程序转换 Lotus Domino 数据

为了使用 request.json.output = object 语法简单地完成转换,我们创建了一个自定义转换程序。对于给定对象,Project Zero 将实例化适当的自定义转换程序将对象转换为 JSON。为实现此目的,我们创建了一个类来实现 IConverter(Milestone 1)或 Converter(Milestone 2)接口。清单 11 给出了这个类的代码。


清单 11. 员工用户转换程序

public class EmployeeConverter implements Converter {

	public Object toJson(Object o) {
		Employee person = (Employee) o;
		JSONObject json = new JSONObject();
		
		// the person document's fieldname value pairs have already been
		// stored as a hashmap when the document was originally read
		// simply convert the contents into JSON
		
		Iterator it= person.getData().
			keySet().iterator();

		while(it.hasNext()){
			String param= (String)it.next();
			json.put(param,
					(String)person.getData().get(param));
		}

		return json;
	}

…

request.json.output 赋值操作中的传入对象将作为 toJson 函数中的参数提供。然后,我们将创建一个 JSONObject 来存储已保存在 Employee 对象中的名称/值对。联系人的 JSONObject 表示如清单 12 所示。


清单 12. Person 文档中的数据的 JSON 表示

{
      "FirstName": "Van",
      "FullName": "CN=Van Staub\/O=IBM",
      "HTTPPassword": "(355E98E7C7B59BD810ED845AD0FD2FC4)",
      "InternetAddress": "vstaub@ibm.com",
      "LastName": "Staub",
      "OfficeCity": "Atlanta",
      "OfficePhoneNumber": "(123) 456-7890",
      "OfficeState": "Ga",
      "OfficeStreetAddress": "4111 Northside Pkwy",
      "ShortName": "vstaub"
}

JSONObject 并不局限于名称/值对。您可以使用字符串、数字、值、数组和其他对象创建复杂对象。举例来说,清单 13 是 Lotus Domino 服务器上的数据库的一个表示。JSONObject 含有一个 JSONObjects 数组(JSONArray),其中含有数据库信息。各个对象还包含一个错误状态和错误消息。


清单 13. Lotus Domino 数据库的一个复杂表示

{
   "databases": [      
      {
         "filename": "names.nsf",
         "filepath": "names.nsf",
         "httpurl": "http:\/\/domino.ibm.com\/__85257350001864E6.nsf?OpenDatabase",
         "message": "",
         "notesurl": "notes:\/\/Domino@IBM\/__85257350001864E6.nsf?OpenDatabase",
         "replicaid": "85257350001864E6",
         "status": 0,
         "template": "StdR4PublicAddressBook",
         "title": "IBM's Directory"
      }, 
      {
         "filename": "webadmin.nsf",
         "filepath": "webadmin.nsf",
         "httpurl": "http:\/\/domino.ibm.com\/__85257350001883E1.nsf?OpenDatabase",
         "message": "",
         "notesurl": "notes:\/\/Domino@IBM\/__85257350001883E1.nsf?OpenDatabase",
         "replicaid": "85257350001883E1",
         "status": 0,
         "template": "",
         "title": "Domino Web Administrator (7)"
      }
   ],
   "message": "",
   "status": 0
}

配置自定义转换程序

实现了自定义转换程序之后,您需要连接对象和转换程序。为此,可以更新 Project Zero 的配置文件 zero.config。要将转换程序连接到对象,可以使用以下语法:

[/config/json/converters/object=convert_class

举例来说,要连接 Employee 对象与员工转换程序,可以使用以下配置:

/config/json/converters/com.ibm.devworks.Contact=com.ibm.devworks.json. EmployeeConverter

如果您决定为 Lotus Notes 类本身实现转换程序,务必要注意对象的返回类型。如前所述,您可以创建各种类型的 Sessions 来访问 Lotus Domino 数据。根据会话的连接,Lotus Domino 数据的返回类型可能会有所不同。要确保行为的一致性,可以建立自定义转换程序并在其中包含相同 Lotus Domino 资源的各种类型。请参见清单 14。


清单 14. 不同类型的 Lotus Domino 对象需要一个转换程序o

config/json/converters/lotus.domino.Database=
com.lotus.rest.json.converters.DatabaseConverter
config/json/converters/lotus.domino.local.Database=
com.lotus.rest.json.converters.DatabaseConverter
config/json/converters/lotus.domino.cso.Database=
com.lotus.rest.json.converters.DatabaseConverter

使用 Lotus Domino Employees 应用程序

要使用 Lotus Domino Employees 应用程序,您需要在 Lotus Domino 服务器上安装 Eclipse 和 Project Zero 插件。安装好这些插件之后,遵循以下步骤开始使用应用程序:

  1. 在 Eclipse 中,选择 File - Import - General - Existing Projects into Workspace,然后选择 Select Archive File 选项。
  2. 单击 Browse 按钮找到 employee.domino.demo.-all-1.0.zip 文件,然后单击 OK 按钮。
  3. 单击 Finish 按钮导入所选项目。
  4. 选择 Run - Run As - Project Zero Application,创建一个新的运行时配置。启动项目后,使用控制台视图上的 Stop 图标停止应用程序。
  5. 选择 Run - Run - Project Zero Application - employee.domino.demo,编辑运行时配置。分别编辑 Arguments 和 Environment 选项卡上的 java.library.path 和 PATH 变量,并参考本文的 “配置 Project Zero 的 RPC 会话” 一节。单击 Apply 按钮,然后关闭程序。
  6. 打开 config/zero.config 文件。
  7. 更新 config/domino/rest 部分中的设置,应用到您的 Lotus Domino 服务器。请参见清单 15 中的代码。


    清单 15. 配置 Lotus Domino Employees 应用程序
    						
    # mail directory of the Domino server relative
    # to the data directory
    # during contact creation a mail file is also created
    
    /config/domino/rest/mailDirectory = "mail"
    
    
    # field that uniquely identifies people
    # this should conform. to the value passed using REST
    # for example, /employees/[uid] is /employees/vstaub
    
    /config/domino/rest/employeeSearchField = "ShortName"
    
    
    # certifier ID password
    
    /config/domino/rest/certIdPassword = "password"
    

  8. 再次,选择 Run - Run As - Project Zero Application。运行应用程序时将出现一个类似于 “C:/Eclipse_ProjectZero/Workspace/employee.domino.demo running on port 8080” 的消息。
  9. 请求受 Lotus Domino 保护的资源,如 names.nsf。
  10. 登录 Lotus Domino 服务器。
  11. 请求 http://:8080。
  12. 单击 List Employees 按钮开始。

注意,names.nsf 必须为全文索引。您可以使用 Lotus Notes 客户机通过 names.nsf 数据库的 Properties 框中实现此目的。Firefox 客户机已经为 Dojo 部件生成了最稳定的客户端应用。如果一切顺利,您将在浏览器中看到如图 13 所示的 Lotus Domino Employee 应用程序。


图 13. Lotus Domino Employees 应用程序的成功部署

日志记录已经作为 System.out 和 System.err 语句实现。如果出现问题,请查看控制台日志中的信息。

一般技巧

使用 Eclipse 调试。在调试运行中应用程序的有用信息时,检查 Global Context 参数变量。使用调试方法确认了我们的猜测,一些线程用于促进 HTTP 请求,并且各线程都要求 Lotus Notes 初始化。

参阅文档。从 Milestone 1 变更为 Milestone 3 需要移植原始代码。了解已安装的版本和所参阅的任何示例的版本。

从一个示例开始。尝试新特性或集成,方法是从 Project Zero 小组提供的一个工作示例开始。

在单独的应用程序中测试代码。Bug 可能会成为 JVM 或 Project Zero 之间产生细微差别的一个因素,而不一定是由代码产生的。这种方法可帮助您理解该问题并适应使用 Project Zero 的工作方式。

未来计划

本文演示的概念有一个具体的用例;但是,其底层模型是使用 Project Zero 对 Lotus Domino 进行资源建模的一个例子。因此,您可以使用 JSON 表示所有的 Lotus Domino 数据并极大地增强已有应用程序的可用性。一些通用思想可用于编写 Ajax 友好的 IBM Lotus Domino Web Access 页面,用于自动检索用户的邮件文件并将它动态地显示在页面中。另一个想法是在 IBM Websphere Portal 环境中创建响应更快的 Lotus Domino 视图和文档的清单。Project Zero 可以轻松地将新的 Web 2.0 理念结合到已有的 Lotus Domino 概念中,这是令人惊叹不已的。

结束语

借助 Project Zero,我们将一个简单的 Lotus Domino 数据库转换成为了一个功能性的 RESTful 应用程序。我们在之前已经看到,在 Project Zero 框架中结合使用 Groovy 等脚本语言可以极大地改进开发流程。最后,我们查看了基于 REST 的 Domino 应用程序的结果。由于 REST 和 Web 2.0 概念的的普及,任何 Lotus Domino 开发人员都可利用已有概念使用 Project Zero 开发和实现 RESTful Domino 应用程序。在此过程中,您可以通过各种创新方式扩展 Lotus Domino 应用程序的使用。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14751907/viewspace-586489/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/14751907/viewspace-586489/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值