IBM FileNet Content Engine 是 IBM FileNet P8 平台上负责内容管理的核心解决方案。企业数据可以分为三类:
- 结构化数据(Structured Data)- 被数据库应用程序严格管理的数据。例如关系数据库中的数据或 ERP 系统中的数据。
- 半结构化数据(Semi-structured Data)- 被数据库应用程序进行松散管理的数据 . 例如 Email, Messaging。
- 非结构化数据(Unstructured Data)- 不被任何应用程序或数据服务器管理和控制的数据 . 例如员工的家庭住址 , 传真文件 , Microsoft word 文档等。
FileNet Content Engine 对这三类数据进行统一集中式管理,从而提供企业内容管理(ECM – Enterprise Content Management)解决方案。具体功能包括:对物理数据建立逻辑索引;对内容的搜索和存取;版本管理以及不同层次的安全性管理等。同时,它的文档管理功能与 FileNet Process Manager 相结合 , 还可以实现企业工作流和业务过程的自动化实现和管理 .
这里介绍一些 FileNet Content Engine 中的基本概念,将有助于读者阅读和理解下面的内容。
FileNet P8 Domain
FileNet P8 Domain 是一个逻辑上的集合,它包含一系列物理资源(比如 Object Store 等),还包含 Content Engine 服务器,客户端需要通过这些服务器提供的服务来访问同一个 Domain 中的物理资源。同时它还关联到一个或多个 security realm,来完成对用户的身份认证和权限控制。FileNet 中的任一资源,必须属于并只属于一个 Domain。
Object Store
一个 Object Store 是一个独立的、存在于 Domain 的对象。它提供了对资源的访问和存取,这里的资源包括 documents,folders, custom objects, class descriptions, property descriptions, security policies 等等,以及有关这些资源的元数据。Object Store 代表了这些资源在 server 上的存储位置。借助 Object Store 对象提供的方法,开发者可以对 Content Engine Server 中的资源进行操作。
Document
Document 对象表示存在于 Content Engine Object Store 中的一个唯一版本的文档。Document 可以被理解成以下两种形式:
- 用户创建的文档:例如 Microsoft Word 文档、图片、HTML 文件等等。
- 包含用户文档信息的对象:在 Content Engine 中用这个对象来唯一表示一个特定版本的用户文档。
一个 document 可以有零或多个内容元素。如果一个 document 包含内容,它的内容可以存储于 Content Engine 服务器(称为 content transfer element),也可以存贮于外部(称为 content reference element)。
FileNet Content Engine Java APIs 概述
如何在自己的 JAVA 应用程序中访问 FileNet 内容引擎并对其中的内容进行增删改查的操作?FileNet 提供了基于 JAVA 的一整套应用程序接口 (API),借助这些 API,开发者可以完成对 Content Engine Server 的所有操作。这些操作根据应用场景的不同可以分为三类:
- Runtime Application: 这类应用程序在运行时与 Content Engine Server 中的 Object Store 和 Documents 进行互操作。
- Administration Application: 主要负责完成对 server 的配置。
- Metadata Authoring Application: 对原数据进行操作,例如定义 Class, 创建 properties 等。
本文中的例子主要针对 Runtime Application 类的 API,也是最常用的这一类接口进行介绍。
FileNet Content Engine 支持两种通讯协议,EJB 和 Content Engine Web Service (CEWS)。下图说明了这些协议在 Content Engine 中的转换和交互,读者可以对 FileNet Content Engine 对外提供的接口及内部的实现机制有一个架构上的理解。
图 1:Content Engine 应用程序接口架构
![Content Engine 应用程序接口架构](https://i-blog.csdnimg.cn/blog_migrate/f3f05e3b808dbcaf54283a96806fdf07.png)
开发者可以选择任意一种通讯协议与 FileNet Content Engine server 建立连接。接口开发包中的 Connection 对象代表客户端与 FileNet P8 Domain 之间的一个逻辑连接,连接中包含的 URI(Universal Resource Identifier)表明了这个连接的目标服务器地址、端口号以及所采用的通讯协议(EJB 或 CEWS)。
EJB
Content Engine 通过 J2EE 应用服务器中的 EJB 容器对外暴露其功能。Content Engine Java API 通过 EJB 协议与 server 之间通过传递串行化的 Java 对象发送请求并接收响应。基于 EJB 协议的 Java API 是目前可用的最快的交互方式。
EJB 监听器提供了本地(Local)和远程(Remote)接口。对于那些与 Content Engine EAR 包绑定在一起 J2EE 组件(位于一个 EAR 包里),可以直接使用本地接口(local interface)。其他的远程调用则可以通过远程接口(remote interface)来实现。Content Engine API 利用 EJB 借口,通过 J2EE 服务器提供的安全性机制对用户透明的实现了自动安全性身份认证和事务管理。
CEWS
Content Engine Web Service(CEWS)基于 SOAP 协议与 Content Engine 服务器进行交互。CEWS 通过 HTTP 协议将 XML 格式的请求发送到 Content Engine 服务器,并接收 XML 格式的响应。
Web Service 监听器由一个 Servlet 实现,在 J2EE 应用服务器的 web container 中运行。除了不能完成对事务的管理,CEWS 与 EJB 提供了完全相同的功能。Web Service Listener 接收到来自客户端的请求后,将这个 request 转发到 EJB 层,并通过 EJB 与 Content Engine server 进行互操作。
这一节我们用几个例子来说明如何使用 FileNet Java EJB API 对 CE 进行添加 document,读取 document 内容,修改属性以及查询 document 等操作。示例中 FileNet CE 和示例程序均安装在 WebSphere 应用服务器上,且这些 WAS Server 必须共用同一个 LDAP 作为其用户注册 (user registry)。
图 2:示例工程拓扑
![示例工程拓扑](https://i-blog.csdnimg.cn/blog_migrate/1ae7d59bba01f31023ad60b1034da3c2.png)
使用 Content Engine Java API 在运行时需要一些 JAR 包 . 这些 jar 包可以在 FileNet 的安装目录下找到。定制程序需要将这些 jar 文件包含在其 classpath 中。
表 1. 使用 EJB 传输协议的 Content Engine Java API 客户端所需的 jar 包
Jace.jar | FileNet Content Engine Java API 核心 JAR 包 |
log4j-1.2.13.jar | The LOG4J framework for Content Engine Java API logging. |
示例程序是一个 web 应用,在该应用中需要首先定义一个角色用以访问 FileNet CE,程序部署后需要为该角色分配对应的用户或组。这样该应用程序将使用应用服务器提供的安全机制访问 CE 服务器进行身份认证和操作鉴权。分配的用户或组必须存在于将要访问的 FileNet P8 Domain 的 Security Realm 中,且具有相应的操作权限。
清单 1.
<security-role> <role-name>CEUser</role-name> </security-role> |
图 3:为角色分配用户或组
![为角色分配用户或组](https://i-blog.csdnimg.cn/blog_migrate/b1393482fbd339d785af6187908f6e07.png)
//construct connection uri String uriStr = new String("iiop://<host>:<port>"); //get connection Connection conn = Factory.Connection.getConnection(uriStr); //get Domain object; Domain domain = Factory.Domain.fetchInstance(conn,"domainName", null); //get ObjectStore object ObjectStore os = Factory.ObjectStore.fetchInstance(domain, "objectStoreName", null); |
FileNet CE API 提供了一个 Connection 对象,所有的 API 调用都通过这个对象进行传递,同时它还以对用户透明的方式处理调用时的身份认证。<host> 为 FileNet CE 所在的 WAS server 机器名或 IP 地址,<port> 通常为 WebSphere application server 的 bootstrap 端口。在获得 Connection 对象之后可以根据名字进而获得 Domain 对象和 ObjectStore 对象,ObjectStore 对象是其他文档操作的参数。在获得 Connection 对象后并不意味着与 FileNet CE 的连接已经成功,只有在成功获得 Domain 对象之后才能确定。
//create new document Document doc = Factory.Document.createInstance(this.objectStore, "document"); //assemble name String documentTitle = "dWSample"; //set properties doc.getProperties().putValue("documenttitle", documentTitle); // get input Stream from the instantiated object of the document you will save. ByteArrayInputStream input = new ByteArrayInputStream(myDoc.getContent()); ContentTransfer ct = Factory.ContentTransfer.createInstance(this.objectStore); ct.setCaptureSource(input); // Set the document MIME type ct.set_ContentType("application/pdf"); ct.set_RetrievalName(documentTitle); ContentElementList cel = Factory.ContentElement.createList(); cel.add(ct); doc.set_ContentElements(cel); doc.checkin(AutoClassify.DO_NOT_AUTO_CLASSIFY, CheckinType.MAJOR_VERSION); doc.save(RefreshMode.REFRESH); ... |
从以上示例中可以看出 Document 是一个物理文件的集合(ContentElementList),我们可以为一个 Document 对象添加多个文件。
/*retrieve document content by documentId. */ Document doc = Factory.Document.fetchInstance(this.objectStore, new Id(documentId), null); ContentElementList cel = doc.get_ContentElements(); ContentTransfer ct = (ContentTransfer) cel.get(0); int contentSize = ct.get_ContentSize().intValue(); InputStream content = ct.accessContentStream(); byte[] output = new byte[contentSize]; content.read(output); |
清单 5.查询
String sqlText ="SELECT * FROM [BatchReport] WHERE ([ReportType] = 'xxx' AND [ReportName] = 'xx') ORDER BY [DateCreated]"; //preform query SearchSQL sql = new SearchSQL(sqlText); SearchScope search = new SearchScope(this.objectStore); IndependentObjectSet ios = search.fetchObjects(sql, null, null, null); if(ios.isEmpty()){ logger.log(Level.FINE,"no reports found"); } Iterator docIt = ios.iterator(); ... |
SQL 语句的构造可以借助 FileNet Content Engine Enterprise Manager 中的 Content Engine Query Builder 提供的功能。利用 Query Builder 的 Simple View 下提供的图形界面进行 SQL 语句构造,再转到 SQL View 下进行修改和调整,如去掉不需要查询的属性。
图 4:使用 Query Builder 的 Simple View
![使用 Query Builder 的 Simple View](https://i-blog.csdnimg.cn/blog_migrate/62c85415d4e700523867c7ae5c91d281.png)
图 5:使用 Query Builder 的 SQL View
![使用 Query Builder 的 SQL View](https://i-blog.csdnimg.cn/blog_migrate/a09839fb971691be684a4b70ed9295e6.png)
使用 FileNet Java EJB API 或者 FileNet Java Web Service API 均可以在应用服务器环境中开发基于 web 或 EJB 的客户应用程序。但如果客户应用程序为独立的 Java 程序,因为缺少了应用服务器的支持,只能使用 FileNet Java Web Service API 进行开发。下面是一个使用 FileNet CEWS API 进行文档创建、上传、和保存的例子。例子中使用 IBM Rational® Application Developer V6.0 生成的 web client stub。在运行时需要将 WebShpere 应用服务器运行时环境加入该项目。
清单 6. 使用 CEWS API 创建并保存文件
String strRetrievalName = "createDocumentContent"; // We first need to create a Create verb, populate it CheckinAction verbCheckin = new CheckinAction(); CreateAction createVerb = new CreateAction(); createVerb.setAutoUniqueContainmentName(new Boolean(true)); createVerb.setClassId(docClass); ChangeRequestType objChange = new ChangeRequestType(); objChange.setAction(new ActionType[2]); objChange.setAction(0,(ActionType)createVerb); objChange.setAction(1,(ActionType)verbCheckin); …… // Build a list of properties to set in the new document, //also set properties and content data into stub. ModifiablePropertyType[] objInputProps = new ModifiablePropertyType[lngPropCount]; SingletonString objString = new SingletonString(); …… // Send off the request ChangeResponseType[] objResponseArray = null; ExecuteChangesRequest objRequest = new ExecuteChangesRequest(); objRequest.setRefresh(new Boolean(true)); objRequest.setChangeRequest(new ChangeRequestType[1]); objRequest.setChangeRequest(0,objChange); try { if(fnStub instanceof FNCEWS35SoapBindingStub){ FNCEWS35SoapBindingStub fnSoapStub = (FNCEWS35SoapBindingStub)fnStub; fnSoapStub.setUsername(userAccount); fnSoapStub.setPassword(userPassword); objResponseArray = fnSoapStub.executeChanges(objRequest); }else{ throw new Error("Unable to locate a correct binding."); } }catch(Exception ex){……} return true; |
由于 WebSphere 客户端不支持 MIME 和 DIME 附件,所以上传的文件必须使用 InlineContent 类型 . 下面是一个用 CEWS 构造出的 SOAP 报文的实例。
清单 7. 与 FileNet 建立连接
<soapenv:Header> <Security xmlns="http://schemas.xmlsoap.org/ws/2002/12/secext"> <hd:UsernameToken xmlns:hd="http://schemas.xmlsoap.org/ws/2002/12/secext"> <hd:Username>jim</hd:Username> <hd:Password>pwd4jim</hd:Password> </hd:UsernameToken> </Security> …… </soapenv:Header> <soapenv:Body> <p596:ExecuteChangesRequest refresh="1" xmlns:p596="http://www.filenet.com/ns/fnce/2005/02/ws/schema"> <p596:ChangeRequest id="1"> <p596:TargetSpecification classId="ObjectStore" objectId="CEStore" /> <p596:Action xsi:type="p596:CreateAction" classId="Document" autoUniqueContainmentName="1" xmlns:p596="http://www.filenet.com/ns/fnce/2005/02/ws/schema" /> <p596:Action xsi:type="p596:CheckinAction" xmlns:p596="http://www.filenet.com/ns/fnce/2005/02/ws/schema" /> <p596:ActionProperties xmlns:p596="http://www.filenet.com/ns/fnce/2005/02/ws/schema"> <p596:Property xsi:type="p596:SingletonString" propertyId="DocumentTitle"> <p596:Value>dWSample</p596:Value> </p596:Property> <p596:Property xsi:type="p596:ListOfObject" propertyId="ContentElements"> <p596:Value classId="ContentTransfer" dependentAction="Insert"> <p596:Property xsi:type="p596:SingletonString" propertyId="ContentType"> <p596:Value>text/plain</p596:Value> </p596:Property> <p596:Property xsi:type="p596:SingletonString" propertyId="RetrievalName"> <p596:Value>createDocumentContent</p596:Value> </p596:Property> <p596:Property xsi:type="p596:ContentData" propertyId="Content"> <p596:Value xsi:type="p596:InlineContent"> <p596:Binary>DQpUaGlzIHByb2dyYW0gaXMgZ</p596:Binary> </p596:Value> </p596:Property> </p596:Value> </p596:Property> </p596:ActionProperties> …… </p596:ChangeRequest> </p596:ExecuteChangesRequest> </soapenv:Body> |