http://www.ibm.com/developerworks/cn/lotus/domino-wps/
2009 年 9 月 04 日
Lotus Notes/Domino 和 WebSphere Portal 是 IBM Lotus 家族中的两大产品。本文将向读者介绍一个 WebSphere Portal 与 Lotus Notes/Domino 集成的实例 - 如何创建一个迷你型的日历 Portlet。该 Portlet 能够从 Lotus Domino 中获取用户的会议数据并将其显示出来,其大小可以改变。用户还可以通过显示出来的会议记录访问其在 iNotes 上的具体内容。
实例场景
本文将向读者展示一个 WebSphere Portal 与 Lotus Notes/Domino 集成的实例 -- 创建迷你型日历 Portlet,主要有以下功能:
可变化大小。
能够从 Lotus Domino Server 获取用户的日历数据。
点击日历上某一日期,显示当日的会议邀请记录列表。用户点击列表上的任一条目可以在 iNotes 上打开该条目的详细情况。
读者至少需要拥有 Lotus Domino Server V7 以上 , WebSphere Portal V6 以上,并完成 Portlet 与 Domino Server 之间单点登陆的配置。
关于 Portlet 与 Domino Server 之间的单点登陆,在文章 IBM / Lotus Domino 与 WebSphere Portal: 单点登录 中作了具体的讲解。
如果实际客户环境使用 LDAP 服务器作为 Domino Directory,读者需要配置 Domino Server 和 IBM WebSphere Portal,使得 LDAP 上的用户能够登陆 IBM WebSphere Portal。
由于 WebSphere Portal 和 Lotus Domino 之间的单点登录是通过一种称为轻量级第三方认证 (LTPA) 的机制来实现的。因此,还需要配置 WebSphere Portal 和 Domino 之间的 LTPA 关系。这些在 WebSphere Portal Info center上都有详细的文档,读者可以参考这些材料完成配置。
下面将介绍该实例的原理图,如图 1 所示。部署在 WebSphere Portal 上的 Portlet,显示一个日历,并通过 DIIOP 去访问 Domino Server 得到选定的日期的会议邀请等数据,并在 Portlet 上显示出来。
图 1. 实例原理图
回页首
创建日历
可能大家都知道,用 JavaScript 创建一个日历,简单而快捷。由于在互联网上相关的实例和讲解众多,这里我只是简单的介绍一下日历实现的主要思想。
首先创建一个 7 × 7 的表格,第一行填充 [ 星期一,…,星期天 ],如图 2 所示。然后按照下面的规则填充表格单元。
得到该月 1 日是星期几(假如是第 X 列),然后从第 2 行第 X 列开始填充 [1,2,…,Y] (假如该月有 Y 天)。
填充第 2 行第 1 列到第 X-1 列。
填充剩下的表格单元。
图 2. 日历示意图
各个表格单元的属性可以通过样式文件来显示,例如周末用红色背景,今天用白色背景并加粗等等。
回页首
Portlet 与 Domino Server 之间的通讯
此实例一个重要的功能是能够获取 Domino Server 上的会议记录。在我们的实例当中,我们通过 DIIOP 访问 Domino。因此,我们必须确保所使用的 Domino Server 启动了 DIIOP 服务。具体方法是:打开 Domino 安装目录下的 notes.ini,将 DIIOP 到 ServerTasks 中,保存文件并重启 Domino Server。
下面介绍如何获取 Domino Server 上用户的会议邀请等。前文已经提到,我们的应用中,一个重要功能是对 SSO 的支持。用户登陆了 WebSphere Portal 之后,我们需要根据 WebSphere Portal 上的登陆信息找到用户在 Domino Server 上的信息(Mail Server 和 DIIOP 端口是用户单独配置的,具体见创建 Portlet 小节),最后获取 Domino Server 上该用户的会议邀请纪录,这里主要包含几个重要的步骤:
获取 LTPA cookie 信息。清单 1 中变量 sessionToken 存储的值是 LTPA cookie 信息,将作为方法 getItemByDateUsingLTPA ( 见清单 3) 的第二个参数来获取会议邀请纪录。
获取用户的 Notes 账号。清单 3 中开始部分根据 LTPA cookie 信息获取 Session notesSession,然后根据语句 notesSession.getUserName() 来获取用户的 Notes ID。
获取用户的邮件在服务器上存放位置。一旦获得用户 Notes ID (Domino Server 由用户自行配置),我们就可以通过清单 2 获取用户的邮件存放位置,返回的结果类似于 “mail/test.nsf”。
获取用户的会议邀请纪录。清单 3 演示了如何通过 LTPA 信息来获取用户会议邀请条目。其中, CalendarItem 存放的数据结构如清单 4 所示。
清单 1. 获取 LTPA cookie 信息
Cookie[] cookies = null;
String sessionToken = null;
cookies = renderRequest.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++){
if (cookies[i].getName().equals("LtpaToken")){
sessionToken = cookies[i].getValue();
break;
}
}
}
清单 2. 获取用户邮件存放位置
public static String getMailFile(Session notesSession,
String nameServer, String username) throws NotesException {
String mailFile = null;
Database nameDB = notesSession.getDatabase(nameServer, "names.nsf");
if(nameDB != null) {
String searchCondition = "Owner = \""+
username+"\" & Type= \"Person\"";
DocumentCollection dc = nameDB.search(searchCondition);
if(dc != null) {
Document doc = dc.getFirstDocument();
if(doc != null) {
Vector
mailfiles = doc.getItemValue("MailFile");
if(mailfiles!=null && mailfiles.size()>0)
mailFile = (String)mailfiles.get(0);
}
nameDB.recycle();
if(mailFile != null) {
if(!mailFile.contains(".nsf"))
mailFile += ".nsf";
mailFile = mailFile.replaceAll("\\\\", "/");
}
}
}
return mailFile;
}
清单 3. 获取用户会议邀请条目
/**
* Retrieve the specific calendar items list
* @param mailServer: mail server and DIIOP port,
* e.g. dominodemo.company.com:63148
* @param ltpaToken: LTPA token
* @param startDate: start date, e.g. "03/23/2009"
* @param endDate: end date, e.g. "03/23/2009"
* @return calendar items list.
*
*/
public static List getItemByDateUsingLTPA(String mailServer,
String ltpaToken, String startDate, String endDate) {
ArrayList list = new ArrayList();
Database mailDB = null;
Session notesSession = null;
try {
notesSession = NotesFactory.createSession(mailServer, ltpaToken);
String []tmpArray = mailServer.split(":");
String directory = getMailFile(notesSession,tmpArray[0],
notesSession.getUserName());
mailDB = notesSession.getDatabase(tmpArray[0], directory);
String searchCondition = "@IsAvailable(" +
NotesCalendarFields.calendarDateTime +
") & @IsMember(\"" + startDate
+ "\";@Explode(StartDateTime; \",\"))";
DocumentCollection dc = mailDB.search(searchCondition);
Document doc = dc.getFirstDocument();
long startDateTime = format.parse(startDate).getTime();
long endDateTime = format.parse(endDate).getTime();
while(doc != null) {
Vector vec =
doc.getItemValue(NotesCalendarFields.calendarDateTime);
for( int i = 0; vec != null && i < vec.size(); i ++) {
Date date = ((DateTime)vec.get(i)).toJavaDate();
long elem = date.getTime();
if(elem >= endDateTime)
break;
else if(elem >= startDateTime) {
CalendarItem calItem = new CalendarItem();
if((vec = doc.getItemValue(NotesCalendarFields.subject))
.size() > 0)
calItem.setSubject(vec.get(0).toString());
calItem.setStartDate(date);
vec = doc.getItemValue(NotesCalendarFields.endDateTime);
calItem.setEndDate(((DateTime)vec.get(i)).toJavaDate());
if((vec = doc.getItemValue(NotesCalendarFields.description))
.size() > 0)
calItem.setDescription((String)vec.get(0));
if((vec = doc.getItemValue(NotesCalendarFields.calType))
.size() > 0)
calItem.setCalType(Integer.parseInt(vec.get(0)
.toString()));
URL url = new URL(doc.getHttpURL());
//For SSO reason, we need to replace it's host
String strURL = url.toString().replaceAll(url.getHost(),
tmpArray[0]);
calItem.setHttpURL(strURL);
list.add(calItem);
}
}
Document temp = dc.getNextDocument();
doc.recycle(); // recycle the one we're done with
doc = temp;
}
}
catch(Exception e) {
e.printStackTrace();
}
finally {
try {
if(mailDB != null)
mailDB.recycle();
if(notesSession != null)
notesSession.recycle();
}
catch (NotesException e) {
e.printStackTrace();
}
}
return list;
}
清单 4. CalendarItem 数据结构
public class CalendarItem {
private Date startDate;
private Date endDate;
private String subject;
private String description;
private String httpURL;
/**
0 - Appointment
1 - Anniversary
2 - All Day Event
3 - Meeting
4 - Reminder
*/
private int calType;
public CalendarItem() {
subject = "";
description = "";
calType = 0;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getCalType() {
return calType;
}
public void setCalType(int calType) {
this.calType = calType;
}
public String getHttpURL() {
return httpURL;
}
public void setHttpURL(String httpURL) {
this.httpURL = httpURL;
}
}
回页首
创建 Portlet
完成上述几个步骤之后,接下来我们需要创建一个 Portlet 来显示日历,当用户点击某一日期时,Portlet 能够调用清单 2 和 3 中的代码来获取用户的日历邀请条目并显示出来。能够创建 Portlet 的工具很多,IBM 也不缺乏这样的工具,例如 Rational Application Developer(简称 RAD)。用 RAD V7 创建 Portlet 十分简单,如图 3 和图 4 所示,创建一个名为 PortletExmple 的 Portlet,选择查看和编辑两个视图。
在文件 PortletExamplePortletEdit.jsp 中添加 2 个编辑框,让用户输入:Domino Server,DIIOP 端口(默认为 63148)。另添加一个 button 按钮,能够让用户保存数据。如清单 4 所示。
在 PortletExamplePortlet.java 中定义变量如下:
public static final String mailServer = "dominoCalendarmailServer";
public static final String portnumber = "dominoCalendarportnumber";
public static final String saveAction = "saveaction";
在方法 processAction 中实现处理 Portlet 编辑视图中的按钮事件。代码如清单 5 所示。
接下来我们来介绍如何实现 Portlet 查看视图。在文件 PortletExamplePortletView.jsp 中,其主要功能是生成一个带有日历以及选定日期的页面。我们用 preferences.getValue(String, “”) 获取 Domino Server 以及其端口值,然后调用清单 2 方法 getItemByDateUsingLTPA 可以获取会议邀请信息列表,每条记录包含开始时间、结束时间、描述、主题以及 iNotes 上该条目的 HTTP 链接,效果图如图 5 所示。
图 3. 创建 Portlet 示意图 1
图 4. 创建 Portlet 示意图 2
清单 5. Portlet 编辑视图代码
<%@ page session="false" contentType="text/html; charset=gb2312"
import="java.util.*,javax.portlet.*, com.company.*"%>
<%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<%
PortletPreferences prefs = renderRequest.getPreferences();
if (prefs != null) {
String mailServerValue =
prefs.getValue(PortletExamplePortlet. mailServer, "");
String portValue = prefs.getValue(PortletExamplePortlet.portnumber, "");
%>
<%
} else {
%>
Error: PortletPreferences is null.
<%
}
%>
清单 6. Portlet 编辑视图事件处理函数
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
PortletPreferences prefs = request.getPreferences();
try {
if(request.getPortletMode() == PortletMode.EDIT ) {
if(request.getParameter(saveAction)!=null) {
prefs.setValue(mailServer,request.getParameter(mailServer));
prefs.setValue(portnumber,request.getParameter(portnumber));
prefs.store();
}
response.setPortletMode(PortletMode.VIEW);
response.setWindowState(WindowState.NORMAL);
}
}
catch( ReadOnlyException roe ) {
}
catch( ValidatorException ve ) {
}
}
图 5. Portlet 运行效果图
回页首
结论
通过对上述实例的介绍,读者应该对如何实现 Lotus Notes/Domino 与 WebSphere Portal 的集成有一个大概地了解。读者可以结合 Lotus Notes/Domino 和 WebSphere Portal 的优点,开发出一些集成程序,更好的发挥它们的作用和价值,这也是本文的主要目的。