景慎艳
(辽宁对外经贸学院信息技术系,辽宁大连 116052)
随着纸制文档向电子文档的过渡,人们意识到管理电子文档的优势。大量的软件提供商也开发了各式各样的内容管理系统,虽然这些内容管理系统的设计和实现方式各不相同,但是他们都有一个共同的特点,也可以说是缺点,那就是开发的产品都是建立在私有仓库引擎基础之上。这种无法互相兼容不但增加了系统的复杂性,还给系统的扩展和长期维护增加了难度。总的来说,就是内容管理系统的开发缺少一套可遵循的标准和规范,从而导致各系统间的交互、系统的升级、维护非常困难[1]。
Content Repository for Java Technology API(JSR-170)就是为解决上述问题而产生的一套标准接口。
为了满足面向内容的应用需要,JCR规范为数据存储和相关服务定义了一个抽象的模型和一套Java API。JCR的目标不仅包括传统的内容管理系统,更广泛地讲,任何应用都必须处理非结构化的数字资产以及结构化或半结构化的信息。
通过一个简单、通用的API和一个强大的、可扩展的对象类型系统,JCR仓库模型能够高效地访问大型二进制对象和精细结构的分层数据。此外,许多RDBMS和文件系统上的特性已被纳入到知识库中,包括查询、访问控制的支持、版本控制、锁定。这些服务的标准化支持能够使应用更容易地使用这些功能。
JCR所定义的知识库模型如图1所示。
图1 JCR知识库模型
描述了一个知识库 R,具有W0,W1,和 W2这3个工作区。W1的项图包含一个根节点和A,B,C 3个子结点。A包含一个属性D和一个子结点E,其中D是ST RING类型的属性。E包含一个具有二进制类型的属性 I。B包含一个LONG类型的属性F和一个BOOLEAN类型的属性G。结点C包含一个DOUBLE类型的属性H[2]。
该模型就像是一个具有n个分支的树,它由单一的知识库构成,每个知识库中可以拥有一个或多个工作区,每个工作区都包含一个项目树,项目树中包含结点和属性,结点可以具有一个或多个子结点以及一个或多个属性。实际的数据都是保存在属性中,而结点相当于实际数据所保存的路径。
每个节点都有且只有一个主节点类型。主节点类型定义了节点的特征,例如允许节点拥有的属性和子节点。除了主节点类型之外,节点还可以有一个或多个混合(mixin)类型。混合类型更像修饰器,向节点提供额外的特征。
具体来说,JCR实现可以提供3种预定义混合类型:
mix:versionable:允许节点支持版本管理;
mix:lockable:支持节点的锁定功能;
mix:referenceable:提供自动创建的jcr:uuid属性,给节点一个惟一可以引用的标识符。
文中以淘宝商店(TaobaoShop)为例,说明基于JCR的内容管理系统的设计与实现。
T aobaoShop的存储结构拓扑图如图 2所示[3]。
图2 TaobaoShop的存储结构拓扑图
“taobao:shopCatagory”结点代表店铺的类别,例如服装、电器、家居等。店铺类别结点包含一个“taobao:catagoryName”(类别名称)属性。
“taobao:shop”结点是“taobao:shopCatagory”的子结点,代表店铺。店铺包含“taobao:shopName”(店铺名称)属性。
“taobao:product”结点是“taobao:shop”的子结点,代表某一店铺的商品。包含“taobao:productName”(商品名称)、“taobao:productPrice”(商品价格)以及“taobao:productIntroduction”(商品介绍)属性。
“taobao:transRecord”结点是“taobao:shop”的子结点,代表某一店铺的交易记录,包含“taobao:orderId”(订单编号)、“taobao:order-Price”(订单金额)以及“taobao:orderAddress”(订单地址)属性。
上面描述了TaobaoShop的存储结构,下面利用JCR的开源实现Jackrabbit来实现上面的存储模型。
2.2.1 获得知识库
要访问知识库,首先必须要获得一个知识库[4]。
String configFile="repository.xml";
String repHomeDir="repository";
Hashtable env=new Hashtable();
env.put(Context.INITIA L_CONTEXT_FACTORY,
"org.apache.jackrabbit.core.jndi"+".provider.DummyInitialContextFactory");
env.put(Context.PROVIDER_URL,"localhost");
InitialContext ctx=new InitialContext(env);
RegistryHelper.registerRepository(ctx,"repo",configFile,
repHomeDir,true);
//得到知识库
Repository repository=(Repository)ctx.lookup("repo");
2.2.2 获得工作区
要访问知识库,必须要登录到知识库中的某一工作区。
SimpleCredentials cred=new SimpleCredentials("userid",
"password".toCharArray());
Session session=repository.login(cred,null);
//获得默认工作区
Workspace ws=session.getWorkspace();
//获得工作区ws的根节点
Node rootNode=session.getRootNode();
2.2.3 将内容添加到JCR仓库中
//在根节点下添加"taobao:shopping"子结点。
NodetaobaoShopping= rootNode.addNode("taobao:shopping");
//在"taobao:shopping"结点下添加"taobao:shop-Catagory"子结点。
Node shopCatagory=taobaoShopping.addNode("taobao:shopCatagory");
//为"taobao:shopCatagory"结点定义"taobao:catagoryName"属性和属性值。
shopCatagory.setProperty("taobao:catagoryName","服装");
//在服装类别下添加一个店铺子结点。
Node shop=shopCatagory.addNode("taobao:shop");
//定义店铺名称
shop.setProperty("taobao:shopName","上海淘乐服装店");
//为"上海淘乐服装店"添加商品子结点
Node product=shop.addNode("taobao:product");//定义商品名称
product.setProperty("taobao:productName","兰兰S01男装");
product.setProperty("taobao:productPrice",129.00);
product.setProperty("taobao:productIntroduction","商品详细描述");
//为"上海淘乐服装店"添加交易记录子结点
Node transRecord=shop.addNode("taobao:transRecord");
//定义订单编号
transRecord.setProperty("taobao:orderId","2011030400000001");
//定义订单纵金额
transRecord.setProperty("taobao:orderPrice",569.30);
//定义订单物流信息
transRecord.setProperty("taobao:orderAddress","北京市海淀区xxxxxx");
//保存添加的所有内容
session.save();
2.2.4 取得内容
可以通过获取NodeIterator(它返回特点节点的所有子节点)来浏览知识库中的所有内容。下面代码可以获取到某一店铺的所有商品信息[5]。
Node shopping=rootNode.getNode("taobao:shopping");
Node shopCatagory=rootNode.getNode("taobao:shopCatagory");
NodeIterator it=shopCatagory.getNodes("taobao:shop");
while(it.hasNext()){
Node entry=it.nextNode();
System.out.println(entry.getName());System.out.println(entry.getProperty("taobao:shop-Name").
getString());
}
2.2.5 用XML导入和导出内容
JSR-170为了确保跨 JCR实现的移植性已经做了许多工作。它促进移植性的方式之一就是使用标准的XML导入和导出特性。通过使用这些工具,符合规范的供应商仓库内容可以很容易地转移到另一个符合规范的供应商仓库。使用XML进行序列化的另一个优势是:可以用传统的XML解析工具操纵导出的仓库[6]。
File outputFile=new File("shopping.xml");
FileOutputStream out=new FileOutputStream(outputFile);
session.exportSystemView("/taobao:shopping",out,false,false);
可以把生成的XM L文件导入到另一个知识库[7]。
File inputFile=new File("shopping.xml");
FileInputStream in=new FileInputStream(input-File);
session.importXML("/",in,ImportUUIDBehavior.IMPORT_UUID_CREA TE_NEW);session.save();
2.2.6 添加二进制内容
“taobao:productIntroduction”属性的类型为“nt:file”,也就是说该属性可以保存二进制流。
下面的代码可以把图片文件以二进制流的形式保存到“taobao:productIntroduction”属性中[8]。
File file=new File("兰兰S01男装.gif");
MimeTable mt=MimeTable.getDefaultTable();
String mimeType=mt.getContentTypeFor(file.getName());
if(mimeType==null)mimeType="application/octet-stream";
product.setProperty("jcr:mimeType",mimeType);
product.setProperty("jcr:encoding","");
product.setProperty("jcr:data",new FileInput-Stream(file));
Calendar lastModified=Calendar.getInstance();
lastModified.setTimeInMillis(file.lastM odified());
product.setProperty("jcr:lastModified",lastModified);
通过对淘宝商店的设计,可以感受到通过JCR建立内容存储仓库,有效地降低了系统开发、维护成本,可以大大降低企业在不同底层存储之间的数据迁移成本。随着社会信息化程度的提高,内容管理应用的领域将会越来越多。应用不断增多,对于公共的、标准的内容仓库API的需求也将变得日益强烈。
[1]孔佳,李昀.内容管理系统的产生与发展[J].农业网络信息,2008,23(3):89-92.
[2]于明达.NET多层架构在内容管理系统的设计研究[D]:[硕士学位论文].大连:大连海事大学,2009.
[3]薛胜军,成敏.Java内容仓库及其在CMS中的应用[J].计算机技术与发展,2009,19(1):241-244,247.
[4]张亚男,王鑫.基于内容管理的网站构建系统的研究[J].硅谷,2010,9(23):85-86.
[5]Sunil Patil.Advanced Java Content Repository API[EB/OL][2011-01-06].http://www.onjava.com/pub/a/onjava/2006/11/08/advanced-java-content-re.
[6]黎文导,卢瑜.J2EE平台上基于XML数据交换系统的设计与实现[J].长春工业大学学报:自然科学版,2006,27(4):350-353.
[7]佚名.Java Content Repository API简介[EB/OL][2011-01-06].http://www.ibm.com/developerworks/cn/java/j-jcr/.
[8]张建,刘更,贺朝霞,等.基于XM L实现Java内容仓库和关系型数据库的双向映射[J].计算机应用研究,2009(1):211-214.