Java内容仓库规范及产品介绍
2010-07-29 00:00:00 来源:WEB开发网背景:
对于大多数的应用系统而言,保存信息无疑是最重要也是最平常的功能,目前大多数情况下这些信息是保存在 Oracle、DB2、SqlServer等关系型数据库中的。但是这些数据库在处理图像、文档等二进制数据方面,却是有很多的不足。虽然我们可以用文件系统来替代,例如淘宝就开发了自己的文件系统(Taobao File System),能够满足高性能的存取海量小文件以及PB级数据量和百亿级数据规模的需求,但是对于文件系统而言,他们没有提供用于搜索信息的查询语言,也没有提供关系、事务等相关的功能。而随着应用程序的不断扩展,允许第三方访问这些存储数据已经成为一个典型的需求。
同时很长一段时间以来市场上各个厂家开发的不同的CMS系统,这些系统都建立在他们各自的内容仓库之上,每个CMS 开发商都提供了他们自己的API来访问内容仓库。这对应用程序的开发者带来了困扰,因为要学习不同的开发商提供的API,同时代码也与这些特定的API产生了绑定。JSR-170正是为解决这一问题而出现的,它提供了一套标准的API来访问任何数据仓库。通过JSR-170,你开发代码只需要引用 javax.jcr.* 这些类和接口。它适用于任何兼容JSR-170规范的内容仓库。
JCR(或者JSR-170)规范:
1. JCR模型介绍:
Java内容仓库(Java Content Repository,JCR)试图以独立于具体实现的方式解决这些问题。不论底层资源(如,数据库,本地或虚拟文件系统)是什么,API都将相同。在数据存储之上,JCR提供诸如访问粒度控制、版本控制、内容事件、全文检索和过滤等内容服务。如图(1-1)所示:
图(1-1)
Java内容仓库使用“树结构”保存信息,提供了几个操作数据的特性。树由节点和属性组成,如图(1-2),圆圈代表节点,方框代表属性。1个节点有且只有1个父亲,有任意数目的孩子(子节点)和任意数目的属性。1个属性有且只有一个父亲(它是节点),它没有子节点,由一个名字和一个或多个值组成。属性值的类型可以是:布尔(Boolean)、日期(Date)、双精(Double),长整(Long),字符串(String)或流(Stream)。只有属性可以被用来存储信息,节点则被用来创建树内部的“路径”。在某种程度上,这棵树类似文件系统的结构,节点是目录,属性是实际的文件。
查看原图(大图)
图(1-2)
从上面的图中不难发现,根节点下有多个子节点a、b、c,每个子节点下面又会有多个子节点或属性。例如:a节点下有两个子节点d和e,而e含有两个属性节点j和k,属性j包含了一幅图片,属性k为一个浮点数字;同样的属性g包含了一段字符串而属性h则包含了一个整型数字。
上面图中的每个节点都可以通过他们在层次结构中的绝对路径来唯一标识。例如:“/”可以定位到根节点,而路径/a/d/i则引用了值为“ture”的属性 i。同时绝对路径总是以“/”开始的,而相对路径则是以层次中的某个节点为参考物的。例如:相对于/a而言,我们可以通过d/i来定位到值为“true” 的属性i。
从对象关系角度上看,因为节点和属性含有很多共性的同时又有各自的特点,因而他们在扩展了Item接口的同时增加了自己独特的方法。我们可以用UML图(1-3)来表示他们之间的关系:
图(1-3)
从UML图中我们不难看出,Node和Property都是Item的子类,每个Property节点有且只有一个Node类型的父节点,而每个Node节点只能有0个(根节点)或一个Node父节点,以及多个Item子节点。
2.节点类型
每个Node节点都必须要有,并且只有一个主节点类型,该类型定义了名称、类型、属性以及该节点必须要有和允许有的子节点。每个节点都有一个名称为jcr:primaryType的属性记录它的主节点类型。
除了主节点类型之外,每个节点可以有一个或者多个混合类型,来为特殊节点定义主类型约束之外的特性。当一个节点被指派了一个混合类型之后,它就需要有一个名称为:jcr:mixinTypes的多值属性来记录它的混合节点类型。
满足级别一的实现需要提供获取节点的节点类型的接口及获取仓库中可访问的节点类型定义的接口;满足级别二的实现需要提供为节点指定主节点类型和混合类型的接口。
规范并没有提供定义、创建、管理主节点类型和混合类型的相关接口,但是提供了一组预定义的节点类型。
3.同名兄弟节点
节点是否允许含有同名兄弟节点是通过父节点的节点类型定义来限定的,虽然规范提供了一组必须要实现的节点类型,但是这些类型中没有允许含有同名兄弟节点的定义,因而可以提供一个不支持同名兄弟节点的内容仓库实现。
对于支持同名兄弟节点的仓库实现来说,可以通过Node.getNodes(String namePattern)方法来获取当前节点的子节点中满足namePattern模式的所有节点的迭代器。对于同名节点组中的某个节点,可以通过在路径中用类似数组的语法进行定位。例如,路径/a/b[2]/c[3]可以定位到根节点下a子节点的第二个名字为b的子节点下的第三名字为c的子节点。注意,索引是从一开始的,而不是从零开始。这种方式来源于xpath,但与xpath语法不同的是,内容仓库中的路径不需要显示指定索引值“1”。例如:/a /b/c和/a[1]/b[1]/c[1]是等价的。
索引值取决于Node.getNodes()方法返回的迭代器中子节点的顺序。例如:通过getNodes方法返回如下顺序的子节点[A, B, C, A, D],这中情况下,A[1]表示列表中的第一个节点,A[2]表示列表中的第四个节点。
注意:属性不能含有同名兄弟节点。
4.排序子节点
和同名兄弟节点一样,排序子节点也是可选的实现内容,这取决于仓库实现的节点类型集合。对于支持排序子节点的实现来说,子节点的顺序是和 Node.getNodes()方法返回的迭代器中的顺序相对应的,我们可以通过Node.orderBefore方法来改变顺序。当向一个支持排序子节点的节点增加新子节点的时候,新子节点会被增加到子节点列表的最后。对于一个即支持排序子节点又支持同名兄弟节点的实现来说,我们可以像排序其它子节点一样来对同名兄弟节点进行排序。例如:对于如下的子节点顺序[A, B, C, A, D] ,调用orderBefore("A[2]","A[1]")方法会将A[2]子节点移动到A[1]子节点的前面,结果会是这样:[A, A, B, C, D] 。结果中的第一个A之前在C之后,第二个A之前在列表的最前面;同时他们的索引值也发生了改变,之前的A[1]现在是A[2],而之前的A[2]现在是 A[1]。
对于不支持排序子节点的实现来说,应用程序不应该依赖于Node.getNodes()方法返回结果中的子节点的顺序,因为这个顺序是随时可变的,除非在调用read方法或者整个session生命周期中同名兄弟子节点保持他们之间的相对顺序。
5.属性
属性类型有STRING、、BINARY、DATE、LONG 、DOUBLE 、BOOLEAN、NAME、PATH、REFERENCE几种,可以用处理java中相关类型一样的方式处理他们。
属性节点父节点的类型决定了属性是否能够支持多值属性,这同样是可选的。对于多值属性来说,可以通过Property.getValues() 方法来获得属性值对象的数组。多值属性中所有的值都是同类型并且是排序好的。如果我们将多值属性中的某个属性值设置为null,那么相当于从属性值数组中删除了这个值,同时属性值数组会自动压缩。对于多值属性调用Property.getValue方法或者对单值属性调用 Property.getValues方法都会抛出ValueFormatException。
NAME、PATH、REFERENCE三种类型的属性有特殊的语法。NAME属性被用来存储命名空间标识符;PATH属性代表了工作空间中的一个相对或者绝对路径;REFERENCE属性提供了一个到工作空间任何位置节点的引用,该属性的值是被引用节点的UUID值。
6. 实现级别
从仓库实现功能上来说,可以分为以下几个级别,如图(1-4):
a) 对于所有实现,级别1是必须的,它提供对仓库的读访问,即:对节点和属性的读访问;对属性值的读访问;输出到XML/SAX;支持XPATH语法的查询服务;可访问节点的获取;访问控制权限的获取。
b) 提供写功能:增加和移除节点和属性;对属性值的写操作;持久化命名空间的改变;从XML/SAX导入数据;分配节点的节点类型。JCR的实现并不要求达到级别2或者更高层次。
c) “可选”级别包含一些高级特性,它并不是读写仓库所必需的。包括:事务(它使仓库有可能与JMS或JDBC资源一起工作);版本标定(允许仓库记录节点的不同状态,以备日后检索);事件(允许仓库内发生的任何活动都会被通知给客户端);锁(可以冻结部分树的功能,可以有效地返回一个只读的子树);sql查询语法的支持。
图(1-4)
7. JCR API
使用JCR API时,为了更容易的完成JCR更换,同时尽可能的减少代码变动,建议使用来自javax.jcr包的接口。
Jcr的包结构介绍如下表:
包名 | 作用 |
Javax.jcr | 提供java技术下内容管理的接口和类 |
Javax.jcr.lock | 提供内容管理锁功能需要的接口和类 |
Javax.jcr.nodetype | 提供对内部节点操作相关的功能 |
Javax.jcr.obeservation | 提供事件订阅处理相关的功能 |
Javax.jcr.query | 提供内容查询相关的功能 |
Javax.jcr.query.qom | 提供内容查询的对象模型定义 |
Javax.jcr.retention | 提供保持管理相关的功能 |
Javax.jcr.security | 提供访问控制管理相关的功能 |
Javax.jcr.util | 提供通用帮助类 |
Javax.jcr.version | 提供版本控制相关的功能 |
在jcr中,一个Repository对象代表了整个仓库,客户端可以通过Repository.login方法连接到仓库,连接时可以指定一个工作空间和相关凭证。Login方法返回一个Session对象,它代表客户端和仓库之间的连接,该对象同时还封装了登录用户的授权集合以及到可访问工作空间的绑定。工作空间与Session之间是一一对应的关系,我们可以把工作空间看作是当前用户授权集合下能够访问到的内容实体的视图。
下面的代码展示了一种登录到内容仓库的方法:
//获取仓库对象
InitialContext ctx = ...
Repository repository = (Repository)ctx.lookup("myrepo");
//创建一个凭证对象
Credentials credentials = new SimpleCredentials("MyName", "MyPassword".toCharArray());
// 获取Session对象
Session mySession =repository.login(credentials, "MyWorkspace");
在上面的示例中,我们通过JNDI获取到仓库对象,然后创建了一个简单的凭证对象,进而用这个凭证获取到MyWorkspace的 Session。对于如何获取Repository和Credentials对象规范中并没有做相关约束,本示例只是展示了其中一种可能的方式。
其它比较常用的接口如下:
Node Session.getRootNode():获取根节点,通过该节点可以访问该Session对象有权限访问的所有节点
Node Node.getNode(String relPath):通过相对路径获取到某节点
Node Node.addNode(String node):为Node对象添加子节点
Void Node.remove():删除节点对象
Property Node.getProperty(String relPath):通过相对路径获取属性对象
Void Node.setProperty(String name, String value):为Node对象设置属性值
String Property.getString():获取属性对象的值
Value Property.getValue():获取对属性对象值的原类型封装对象
String Value.getString():获取属性值封装对象的实际数据值
Item Session.getItem(String abspath):通过绝对路径直接定位到某节点对象
Void Session.save():持久化Session对象
Node Session.getNodeByUUID(String uuid):通过全局唯一定位符直接定位到有唯一定位符的节点对象
下面的代码展示了如何使用上述接口:
// 获取根节点
Node root = mySession.getRootNode();
// 定位到某节点
Node myNode = root.getNode("a/e");
// 获取到某个节点的属性
Property myProperty = myNode.getProperty("k");
// 获取属性节点的值
Value myValue = myProperty.getValue();
// 将属性值转换成特定的格式,此处myDouble的值会是6.022 x 10^23
double myDouble = myValue.getDouble();
// 直接获取到值为6.022 x 10^23的属性k
Property myProperty =(Property)mySession.getItem("/a/e/k");
// 假设节点/a/e是可访问的,并且定位符为:1111 2222 3333 4444,通过以下代码可以//得到e节点对象
Node myNode = mySession.getNodeByUUID("1111 2222 3333 4444");
在对节点或者属性做变动的时候,需要注意以下几点:
a) Node和Property发生的改变不会立即持久化到工作空间中,而是暂时存储在Session对象中,直到这些变动被保存或者放弃。可以通过Session或Item对象的save方法保存更改,通过refresh(false)方法来取消更改;Session对象的save方法和 refresh方法会持久化或者放弃与当前session相关的所有改变,而Item的相关方法则只会持久化或者放弃与特定节点相关的改动。在改变被保存之后,其它访问该工作空间的Session便可以发现这些变动。没有被持久化的更改在当前Session对象中是立即生效的,但是却不会被其它访问该工作空间的Session对象察觉。
b) 上面所描述的情况是在没有事务的情况下,如果save或refresh方法在一个事务域中,那么调用save之后,变动也不会立即对其它访问该工作空间的Session对象可见,而是在事务提交之后才会对其它Session可见。同时没有调用save方法的变动不会在事务中提交。
JCR模块实现
Spring中含有一个JCR模块,它的主要目标是以一种类似Spring ORM包的方式,简化使用JSR-170 API开发应用程序。特点如下:
a)JcrTemplate:它是JCR模块的核心类之一,它提供了与JCR会话一起工作的方便方法,将调用者从必须处理的打开和关闭会话、事务回滚(如果底层仓库提供)、以及处理其它特性中的异常等工作中解放出来。它实现了JCR Session的绝大部分方法,允许执行 JcrCallback和异常处理。
b)RepositoryFactoryBean:用于配置、启动和停止仓库实例。支持包含预定义的用于Jackrabbit和Jeceira的FactoryBean,以及一个很容易支持其它仓库的抽象基类。
c)SessionFactory:用来统一Repository,Credentials和Workspace接口,允许自动注册监听器和自定义名字空间。它隐藏了实现内部的认证细节,因此一旦配置完成,使用同一证书的会话可以很容易的被检索出来。
d)OpenSessionInView:拦截器和过滤器允许每个线程跨不同组件使用同一会话。
JSR-170项目发展:
a) Jackrabbit 是JSR-170的参考实现,Apache基金的一部分,提供级别1,2和可选功能。目前它已经有官方公开的发布版本,最新版本是 1.6.2,该版本被认为足够稳定,可以被用在产品环境。此外,Jackrabbit也被用来作为Day Software(JSR-170的领导者)的商业产品的基础。除了实现JSR-170中定义的所有特性,JackRabbit还加入了额外的功能(如 SessionListeners或CustomNode注册),以及JCA连接器、taglib、WebDAV接口、虚拟文件系统和JDBC后端。 JackRabbit的许可证是Apache 2.0。
b) eXo企业内容管理(ECM)是一套Portlets,提供了Web内容、文档和记录管理工具,它构建在eXo JCR之上。 eXo JCR是eXo platoform的一部分,包含规范要求的所有强制特性和几个可选特性。它将内容存储在中央资源库中,规定了结构、版本、锁定及搜索内容的方式。与 eXo平台的其他组件类似,该产品进行了大量优化和扩展,这些都包含在该产品的独立分发包中。eXo JCR支持JDBC兼容数据库,如MySQL、DB2或HSQL(它是缺省的)作为后端存储,它是双许可的(GPL和商用)。与ECM产品一起的是一些预定义的工作流,你可以定制它们以方便地共享和传播公司中的ECM内容和文档。也可以增加自己的工作流定义,并以很多不同的方式来使用它们。eXo ECM对企业内容的捕获、生产、管理、发布和记录提供了强有力的支持。所有的这一切都以高度可定制的方式进行。但是产品背后的主要驱动力却是为了提供易于使用的高级功能,可以模拟真实操作系统中的应用,如文件浏览器。
c) Jeceira 与Jackrabbit和eXo JCR相比,是相对较新的项目。它实现了级别1和2的一些需求,只在写操作时,支持来自可选级别的观察功能。这个项目处于未完成阶段,然而它被 Magnolia(一个流行的基于java的CMS,与作为JSR-170参考实现的Jackrabbit类似)使用。在最终版发布时,它计划包含所有级别的功能,发布时间目前尚不确定。Jeceira的许可证是Apache 2.0,使用HSQL数据库作为它的存储引擎。
Java内容仓库的未来:
虽然JSR-170已经于2005年5月完成,但是Java内容仓库的工作并没有终止。第二版JCR API已经发布了公众评估版(JSR-283),JSR-283作为官方的后继者,将聚焦于功能增强,例如remoting,通过新的标准节点类型(包括元信息和国际化)改进互操作性,客户端/服务器协议映射和扩展内容模型的能力。同时还存在着一些JSR之外的想法和项目:绑定/映射框架(java类和 JCR树之间的相互映射);建构于JCR之上的WebDAV服务器等。目前已经出现了用于不同产品的JSR-170连接器,如Alfresco、BEA Portal Server和IBM Domino。对JCR模块而言,路线图包括用于几个实现的Acegi安全集成,支持Spring 2.0名字空间模式(它将减少XML的配置)和与其它JCR的实现集成。JCR的前景看起来一片光明。
更多精彩
赞助商链接