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

developerWorks 中国  >  Lotus | WebSphere  >

Lotus Notes/Domino 与 WebSphere Portal 集成实践

创建一个迷你型日历 Portlet

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 中级

刘 奇, 软件工程师, IBM
梁 骞, 高级软件工程师, IBM

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,主要有以下功能:

  1. 可变化大小。
  2. 能够从 Lotus Domino Server 获取用户的日历数据。
  3. 点击日历上某一日期,显示当日的会议邀请记录列表。用户点击列表上的任一条目可以在 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. 实例原理图
图 1. 实例原理图




回页首


创建日历

可能大家都知道,用 JavaScript 创建一个日历,简单而快捷。由于在互联网上相关的实例和讲解众多,这里我只是简单的介绍一下日历实现的主要思想。

首先创建一个 7 × 7  的表格,第一行填充 [ 星期一,…,星期天 ],如图 2 所示。然后按照下面的规则填充表格单元。

  • 得到该月 1 日是星期几(假如是第 X 列),然后从第 2 行第 X 列开始填充 [1,2,…,Y] (假如该月有 Y 天)。
  • 填充第 2 行第 1 列到第 X-1 列。
  • 填充剩下的表格单元。

图 2. 日历示意图
图 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<Object> 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<CalendarItem> getItemByDateUsingLTPA(String mailServer, 
String ltpaToken, String startDate, String endDate) {
    ArrayList<CalendarItem> list = new ArrayList<CalendarItem>();
    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<Object> 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
图 3. 创建 Portlet 示意图 1

图 4. 创建 Portlet 示意图 2
图 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" %>
<portlet:defineObjects/>
 
<%
PortletPreferences prefs = renderRequest.getPreferences();
if (prefs != null) {
    String mailServerValue = 
	    prefs.getValue(PortletExamplePortlet. mailServer, "");
    String portValue = prefs.getValue(PortletExamplePortlet.portnumber, "");
%>
 
<FORM ACTION="<portlet:actionURL/>"METHOD="POST">
Mail Server:<INPUT NAME="<%=PortletExamplePortlet. mailServer %>" 
	VALUE="<%= mailServerValue %>" TYPE="text"><BR>
Port:<INPUT NAME="<%=PortletExamplePortlet. portnumber %>"
    VALUE="<%= portValue %>"TYPE="text"><BR>
<input type="submit" value="Save" 
		name="<%= PortletExamplePortlet.saveAction %>" />  
<%
}
 %>
<input type="submit" value="Cancel" 
onclick="javascript:location.href='<portlet:renderURL portletMode="view"/>';"
/> 
</FORM> 
<%
    } 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 运行效果图
图 5. Portlet 运行效果图




回页首


结论

通过对上述实例的介绍,读者应该对如何实现 Lotus Notes/Domino 与 WebSphere Portal 的集成有一个大概地了解。读者可以结合 Lotus Notes/Domino 和 WebSphere Portal 的优点,开发出一些集成程序,更好的发挥它们的作用和价值,这也是本文的主要目的。



参考资料



作者简介

刘奇是 IBM 中国软件工程师,现在 IBM 中国软件开发实验室 Lotus Notes Client 开发中心工作。


梁骞是 IBM 中国高级软件工程师,现在 IBM 中国软件开发实验室 Lotus Notes Client 开发中心工作。




对本文的评价








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