Lotus Domino 作为一种全球领先的协作、消息传递和 Web 支持软件,正在迅速地在中国企事业推广。Domino 已经成为帮助每个人更灵活和更高效地工作的强大支持。如何从 DOMINO 数据库中获取数据,使这些数据为其他系统可用,已经成为许多企业迫切需要解决的问题。然而 domino 不同于普通的关系型数据库,由 ibm/lotus 自主研发,有自己的标准和特殊性,是一种另类的数据库类型。开发具有访问 DOMINO 服务器的应用程序的方法有许多种,但是普遍存在的问题是功能有很大的局限性,都要在依赖于 lotous notes 这一庞大而昂贵的客户端软件。随着 java 语言和 CORBA 中间件技术的日趋成熟,新版本的 Domino 也提供了 corba 服务,使这一问题得到彻底的解决。java/corba 访问 domino 的优点在于采用此技术开发的应用程序在不用安装 lotus notes 的情况下获取远程 domino 服务器上的数据,真正做到了瘦客户端,为企业节省了不必要的开支,同时也极大的降低了应用程序部署的难度。由于这一技术刚刚发展两三年,国内基本上没有相应的中文资料,corba 技术也属于比较高级的开发技术,绝大多数开发工程师都没有接触过,因此许多做数据集成的开发人员对 domino 的开发望而生畏。本文旨在为开发人员提供一个全面的 java/corba 访问 domino 的技术解决方案,并通过例程指导开发人员掌握这一新技术。其中也有笔者在开发过程中的一些经验与教训,相信对广大开发人员一定会有相当大的帮助。
起点R3能够通过以下三种方式来获取Domino数据,第一种方式是Notes本地获取;第二种是DIIOP方式远程获取;第三种是Corba访问Domino,第一第二种方式只能在Windows平台下,第三种方式能够实现跨平台访问Domino。本文分为上下两篇,本文为上篇,先介绍第一第二两种方式获取数据,下篇再介绍第三种方式。
在起点R3的Domino采集器中,有如下代码:
if(properties.getProperty("local")==null){
session = NotesFactory.createSession(((CrawlTask)job.getCrawltask()).getResourceuri() ,((CrawlTask)job.getCrawltask()).getAccount() , ((CrawlTask)job.getCrawltask()).getPassword());
}else{
session = NotesFactory.createSession();
}
即如果传入的插件参数中有local=true,则使用本地Notes访问数据,否则,通过DIIOP访问Domino数据,需要注意的是,起点R3在采集Domion数据的时候需要将Domino或者Notes的安装目录加入到PATH中,获取到session之后,通过以下代码获取数据库信息:
db = session.getDatabase(session.getServerName(), properties.getProperty("database"));
documentList = db.getAllDocuments() ;
然后可以遍历documentList获得数据库中的所有文档。
对Domino数据库的遍历有两种方式,第一种是通过 documentList.getFirstDocument()和documentList.getNextDocument() 顺序获取文档,第二种方式是documentList.getLastDocument()和documentList.getPrevDocument() 逆序获取文档,最终获取到的是Document对象,然后通过以下代码来对Document的域进行遍历:
Vector id = fieldDoc.getItems();
for (int i = 0; i < id.size(); i++) {
try{
Item item = (Item)id.get(i) ;
MetaType metaType = new MetaType(item.getName(),String.valueOf(item.getType())) ;
if (item != null && item.getType() == 1280) { //表示是文本
Vector values = item.getValues() ;
if(values!=null && values.size()>0){
StringBuffer strb = new StringBuffer() ;
for(Object value : values){
if(strb.length()>0){
strb.append(" ") ;
}
if(value!=null){
strb.append(value.toString()) ;
}
}
metaType.setValue(strb.toString()) ;
outputText.getMetadata().add(item.getName(), metaType.getValue()) ;
}else{
metaType.setValue(item.getValueString()) ;
outputText.getMetadata().add(item.getName(), metaType.getValue()) ;
}
//metaType.setValue(item.getValueString()) ;
//outputText.getMetadata().add(item.getName(), metaType.getValue()) ;
} else if (item != null && item.getType() == 1024) { //表示是时间
DateTime dateTime = item.getDateTimeValue() ;
if(dateTime!=null){
metaType.setValue(String.valueOf(item.getDateTimeValue())) ;
outputText.getMetadata().add(item.getName(), metaType.getValue()) ;
}
} else if (item != null && item.getType() == 768) { //表示是整型
metaType.setValue(String.valueOf(item.getValueInteger())) ;
outputText.getMetadata().add(item.getName(), metaType.getValue()) ;
} else if (item != null && item.getType() == 1) { //表示是rtf域
metaType.setValue(item.getValueString()) ;
outputText.getMetadata().add(item.getName(), metaType.getValue()) ;
} else{
Vector values = item.getValues() ;
if(values!=null && values.size()>0){
StringBuffer strb = new StringBuffer() ;
for(Object value : values){
if(strb.length()>0){
strb.append(" ") ;
}
if(value!=null){
strb.append(value.toString()) ;
}
}
metaType.setValue(strb.toString()) ;
outputText.getMetadata().add(item.getName(), metaType.getValue()) ;
}else{
metaType.setValue(item.getValueString()) ;
outputText.getMetadata().add(item.getName(), metaType.getValue()) ;
}
}
dataList.add(metaType);
}catch(Exception ex){}
}
重点是集中特殊域的获取,其中,域类型1280是文本,1024是时间,768是数字,1表示RTF域,还有文本列表域等。
RTF域的获取需要使用以下代码:
if (item != null && item.getType() == 1) { //表示是rtf域
/**
* 处理RTF域中的文本内容
*/
if(properties!=null && "false".equals(properties.getProperty("rtftext"))){}else{
outputText.getMetadata().add(item.getName(), item.getValueString()) ;
}
java.util.Vector vector = document.getItems();
for(int i=0; i
Item itmWjbt = (Item)vector.get(i) ;
if(itmWjbt != null && itmWjbt.getName().equals("$FILE") && zipData.getFileMap().get(itmWjbt.getValueString())==null){
EmbeddedObject eo = document.getAttachment(itmWjbt.getValueString());
InputStream input = eo.getInputStream();
//从input中获取数据保存到文件或其他输出流
input.close() ;
}
}
文本列表域的获取需要使用 Vector values = item.getValues() ,然后
for(Object value : values){
if(strb.length()>0){
strb.append(" ") ;
}
if(value!=null){
strb.append(value.toString()) ;
}
}
在上篇中介绍了从JAVA中获取Domino数据库中的文档信息,但有一个缺点,就是必须要有本地Notes,即限制了只能在Windows下使用,在有些情况下,可能需要在其他操作系统中通过JAVA获取Domino数据,本文即为介绍在JAVA中使用CORBA实现跨操作系统访问Domino。本文介绍的Domino版本应该要在R5以上,R5以下版本未经验证,无法确认是否可行。注意,本文中需要使用lotus.domino.corba包,网络上找到这个库源码非常不容易,作者也是花费了不少时间才从国外一个小网站上下载到,如果有需要,请联系作者。Corba访问Domino过程比较复杂,大致如下:
String ior = http://127.0.0.1/diiop_ior.txt;
Properties props = new Properties();
props.put("org.openorb.orb.core.ORB", "org.openorb.orb.core.ORB");
ORB orb = ORB.init(new String[]{}, props);
// 通过 IOR 得到 IObjectServer 对象
org.omg.CORBA.Object obj = orb.string_to_object(ior);
IObjectServer ios = IObjectServerHelper.narrow(obj);
// 通过 IObjectServer 获得 ISession
ProtocolVersion maxVersion = new ProtocolVersion(IBase.DOM_MAJOR_MINIMUM_VERSION, IBase.DOM_MINOR_MINIMUM_VERSION);
ProtocolVersion minVersion = new ProtocolVersion(IBase.DOM_MAJOR_VERSION,IBase.DOM_MINOR_VERSION);
SessionData sd = ios.createSession(maxVersion, minVersion, ((CrawlTask)job.getCrawltask()).getAccount() , ((CrawlTask)job.getCrawltask()).getPassword());
session = sd.sesObject;
通过账号和密码访问登录Domino后获得session,然后通过session获得数据库信息,如下:
// 使用 ISession
dbCache = session.getDatabase(sd.serverName, properties.getProperty("database"), false);
dCdata = dbCache.db.getAllDocuments();//iDatabase.search("1=1", null, 10);
IDatabaseHolder idatabaseholder = new IDatabaseHolder();
IntHolder intholder = new IntHolder();
IDocument document = dCdata.dcObject.getFirstDocMDB(idatabaseholder, intholder);
一样是或得到document,类型为IDocument,dCdata.dcObject即是文档列表,对此对象遍历即可
document = dCdata.dcObject.getNextDocMDB(document ,idatabaseholder, intholder);
对IDocument对象的遍历也和DIIOP也有一些差别:
ItemData[] id = fieldDoc.getData().items;
for (int i = 0; i < id.length; i++) {
MetaType metaType = new MetaType(id[i].name,String.valueOf(id[i].type)) ;
ItemValue item = id[i].values;
if (id[i].type == 1280) { //表示是文本
metaType.setValue(item.StringValue()) ;
} else if (id[i].type == 1024) { //表示是时间
DateTime dateTime = item.TimeObject() ;
if(dateTime!=null){
metaType.setValue(String.valueOf(dateTime.toString())) ;
}
} else if (id[i].type == 768) { //表示是整型
metaType.setValue(String.valueOf(String.valueOf(item.DoubleValue()))) ;
} else if (id[i].type == 1) { //表示是rtf域
//获得RTF域的附件信息
} else{
metaType.setValue(item.StringValue()) ;
}
}
类型代码和上篇中介绍的都是一样的,但RTF域的附件信息获取方式不太一样,需要对items[i].RTObject对象进行进一步处理,处理过程如下:
IRichTextItem rtf = items[i].RTObject ;
if(item.StringValue()!=null){
outputText.getMetadata().add(items[i].name, item.StringValue()) ;
}else if(rtf.getValueAsString()!=null){
outputText.getMetadata().add(items[i].name, rtf.getValueAsString()) ;
}
{
NameAndObject[] ed = rtf.getEmbeddedObjects() ;
if(ed.length>0){
for(NameAndObject nameObject : ed){
EmbeddedData eo = document.getAttachment(nameObject.name) ;
if(eo!=null && eo.embedObject!=null && eo.embedObject.getFileSize()>0){
BooleanHolder holder = new BooleanHolder(true);
IntHolder inholder = new IntHolder();
byte[] data = null ;
java.io.ByteArrayOutputStream output = new java.io.ByteArrayOutputStream() ;
try{
while((data = eo.embedObject.getFile(inholder, holder))!=null && data.length>0){
output.write(data) ;
}
}catch(Exception ex){
ex.printStackTrace();
}
java.io.ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()) ;
//获得输入流,自定义后续处理过程
input.close() ;
}
}
}
}
最后是关闭session和db, db.recycle();