专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Java教程 » javaspring:集成Java内容仓库和Spring »正文

javaspring:集成Java内容仓库和Spring

来源: 发布时间:星期四, 2008年12月18日 浏览:73次 评论:0
保存各种信息对于应用来说非常平常大多数时候它们是保存在关系数据库中数据库处理规范标准数据类型十分在行但是在处理如图像、文档等 2进制数据时却不是那么得心应手尽管可以用文件系统作为替代——而且它们还提供了更好性能但它们既没有提供用于搜索信息查询语言也没有提供表示关系或事务概念

在很多情况下允许第 3方访问这些存储数据(随着应用不断扩展这成为个典型需求)是个漫长而复杂过程(它们不会在夜的间完成)内部存储结构很容易影响API架构以及信息检索和遍历方式

什么是JSR-170

幸运被称为Java内容仓库(Java Content RepositoryJCR)JSR-170试图以独立于具体实现方式解决这些(以及其它)问题不论底层资源(如数据库本地或虚拟文件系统)是什么API都将相同在数据存储的上JCR提供诸如访问粒度控制、版本控制、内容事件、全文检索和过滤等内容服务由Day Software领导JSR-170背后专家组令人印象深刻包括内容管理系统(CMS)提供商Vignette、Hummingbird Ltd.、Stellent和通用Java驱动解决方案提供商如BEA s、IBM和Oracle该规范标准很可能成为内容管理和文档存储方面事实上标准

经过几乎2年半努力工作最终于2005年6月完成javax.jcr包中API包含了大约50个类(主要是接口和异常)2006年早些时候发布了1.0版本参考实现(JackRabbit)

JSR-170概览

Java内容仓库建立在仓库(除了是“用于安全地保存货物地点”通常含义的外)概念的上它提供了几个操作数据特性仓库使用“树结构”保存信息如下图树由节点和属性组成圆圈代表节点方框代表属性1个节点有且只有1个父亲有任意数目孩子(子节点)和任意数目属性1个属性有且只有个父亲(它是节点)它没有子节点个名字和个或多个值组成属性值类型可以是:布尔(Boolean)、日期(Date)、双精(Double)长整(Long)串(String)或流(Stream)只有属性可以被用来存储信息节点则被用来创建树内部“路径”在某种程度上这棵树类似文件系统结构节点是目录属性是实际文件

仓库功能被划分为几个“兼容性”级别每个级别提供组特定特性:

  1. 级别 1

    对于所有实现级别1是必须它提供对仓库访问简而言的:
    • 对节点和属性读访问
    • 对属性值读访问
    • 输出到XML/SAX
    • 支持XPATH语法查询服务
  2. 级别 2

    级别2提供功能:
    • 增加和移除节点和属性
    • 对属性值写操作
    • 从XML/SAX输入数据
    值得注意JCR实现并不要求达到级别2或者更高层次因此和只读仓库起工作也是完全符合规范标准

  3. 可选级别

    “可选”级别包含高级特性它并不是读写仓库所必需但确是真正为JSR-170增色部分这个级别包括(除了其它的外):
    • 事务 —— 它使仓库有可能和JMS或JDBC资源起工作
    • 版本标定 —— 允许仓库记录节点区别状态以备日后检索规范标准对于这个主题有相当篇幅;该特性使得用JSR-170作为后端构建个CVS克隆成为可能
    • 事件 —— 亦称观察它允许仓库内发生任何活动都会被通知给客户端
    •  —— 可以冻结部分树功能它可以有效地返回个只读子树

API回顾

使用JSR-170时建议使用来自javax.jcr接口这样更换JCR实现时会容易些不会有任何代码变动

API核心类是Session它代表客户端和仓库的间连接使用连接活跃其上workspace名和所提供credentials进行定义Session包含读(级别1)和写(级别2)思路方法;使用底层仓库不支持功能时将抛出异常

这个包还包含了那些组成仓库单元接口定义:WorkspaceCredentialsNodePropertyItem(NodeProperty超类)和Valuejavax.jcr.query包负责处理查询javax.jcr.nodetype包负责定义节点类型剩余包负责可选级别功能javax.jcr.versionjavax.jcr.observationjavax.jcr.lock个有趣包是javax.jcr.util它包含ItemVisitor实现它源自GOF( 4人帮Gang of Four)撰写著名设计模式中访问者模式(Visitor-pattern)接口

JSR-170实现

Google和SourceForge会列出好几页JSR-170实现但是它们中大多数都处于alpha阶段没有发布任何版本以下是个可以自由下载项目列表它们已经被作者使用过:

  • Jackrabbit
    它是JSR-170参考实现Apache基金部分提供级别12和可选功能在撰写本文时它已经经过孵化阶段并有个官方公开发布版本该版本被认为足够稳定可以被用在产品环境此外Jackrabbit也被用来作为Day Software(JSR-170领导者)商业产品基础除了实现JSR-170中定义所有特性JackRabbit还加入了额外功能(如SessionListenersCustomNode注册)以及个有趣捐献来项目套件它包括:JCA连接器、taglib、WebDAV接口、虚拟文件系统和JDBC后端JackRabbit许可证是Apache 2.0
  • eXo JCR
    它是eXo platoform部分包含规范标准要求所有强制特性和几个可选特性最近版本发布(1.0RC7)是2006年6月22日基于规范标准最终草案2eXo JCR支持JDBC兼容数据库如MySQL、DB2或HSQL(它是缺省)作为后端存储它是双许可(GPL和商用)最终版发布日期尚未确定
  • Jeceira
    和Jackrabbit和eXo JCR相比它是相对较新项目它实现了级别1和2些需求只在写操作时支持来自可选级别观察功能不幸这个项目处于未完成阶段在过去9个月没有新版本发布然而它被Magnolia(个流行基于javaCMS和作为JSR-170参考实现Jackrabbit类似)使用在最终版发布时它计划包含所有级别功能发布时间目前尚不确定Jeceira许可证是Apache 2.0使用HSQL数据库作为它存储引擎

JCR模块

Spring Modules部分JCR模块主要目标是:以种类似Spring主分发包中ORM包方式简化使用JSR-170 API进行开发特点如下:

  • JcrTemplate允许执行JcrCallback和异常处理(将需检查JCR异常转换成不需检查Spring DAO异常)这个模板实现了来自JCR Session绝大部分思路方法可以简单地作为替换物使用此外该模板知道线程绑定会话这个会话可以跨几个思路方法使用这在使用事务型仓库时非常有用
  • RepositoryFactoryBean它配置、启动和停止仓库例子JSR-170并没有介绍说明仓库配置标准方式需要注意实现在这个方面区别这个支持包含预定义用于Jackrabbit和JeceiraFactoryBean以及个可以很容易支持其它仓库抽象基类
  • SessionFactory用来统RepositoryCredentialsWorkspace接口允许自动注册监听器和自定义名字空间
  • Spring声明性事务为那些实现了(可选)事务特性仓库提供了支持
  • OpenSessionInView拦截器和过滤器允许每个线程跨区别组件使用同会话JcrTemplate检索、关闭和管理JCR会话工作已经外部化对于者完全透明
本文将使用参考实现(Jackrabbit)由于JCR模块使用javax.jcr接口因此改变实现根本就是个配置问题让我们步地来看看在Jackrabbit的上如何使用Java内容仓库以及如何让Spring模块来帮助完成这工作

配置仓库和SessionFactory

<bean id="repository" ="org.springmodules.jcr.jackrabbit.RepositoryFactoryBean">
<!-- normal factory beans params -->
<property name="configuration" value="path:jackrabbit-repo.xml"/>
<property name="homeDir" ref="./tmp/repo"/>
</bean>


JCR支持提供RepositoryFactoryBean类配置Jackrabbit它需要JackRabbit配置文件和主目录注意RepositoryFactoryBean在使用本地文件系统时特别有用;对于服务器环境仓库可能被注册在JNDI中此时可以使用JndiObjectFactoryBean帮助类(Spring分发包部分)检索它:

<bean id="repository" ="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jcr/myRepository"/>
</bean>


或使用Spring 2.0模式名字空间:

<jndi:lookup id="entityManagerFactory" jndi-name="jcr/myRepository"/>


为了简化和JCR工作模块增加了SessionFactory接口:

public erface SessionFactory {
public Session getSession throws RepositoryException;
public SessionHolder getSessionHolder(Session session);
}


SessionFactory隐藏了实现内部认证细节因此旦配置完成使用同证书会话可以很容易被检索出来为了利用实现特性(没有涵盖在规范标准中)这个接口允许检索SessionHolder它是个JCR模块特定类主要被用于事务和会话管理(通过种可用于每个JCR实现缺省、通用实现)但是它不支持可选特性或自定义特性(如JackrabbitSessionHolder它支持Jackrabbit事务基础结构)JCR模块提供种简易、透明方式来发现SessionHolder实现(这些我将在以后详细提到)使的很容易地插入对JSR-170其它兼容库支持

SessionFactory缺省实现是JcrSessionFactory它要求个进行工作仓库和证书

<!-— SessionFactory -->
<bean id="jcrSessionFactory" ="org.springmodules.jcr.JcrSessionFactory">
<property name="repository" ref="repository"/>
<property name="credentials">
<bean ="javax.jcr.SimpleCredentials">
<constructor-arg index="0" value="bogus"/>
<!-- create the credentials using a bean factory -->
<constructor-arg index="1">
<bean factory-bean="password" factory-method="toCharArray"/>
</constructor-arg>
</bean>
</property>
</bean>

<!-- create the password to it as a char -->
<bean id="password" ="java.lang.String">
<constructor-arg index="0" value="pass"/>
</bean>


这个bean声明非常简单需要注意地方是密码被提供给SimpleCredential构造:它只接受使用Spring工厂声明作为种变通

JcrTemplate

JcrTemplate是JCR模块核心类的它提供了和JCR会话起工作方便思路方法者从必须处理打开和关闭会话、事务回滚(如果底层仓库提供)、以及处理其它特性中异常等工作中解放出来:

<bean id="jcrTemplate" ="org.springmodules.jcr.JcrTemplate">
    <property name="sessionFactory" ref="jcrSessionFactory"/>
    <property name="allowCreate" value="true"/>
</bean>


模板定义非常简单类似来自Spring框架其它模板类HibernateTemplate

例子

既然仓库已经配置了接下来看看“Spring化”例子的它来自Jackrabbitwiki页:

public Node importFile(final Node folderNode, final File file, final String mimeType,
final String encoding) {

(Node) execute( JcrCallback {
           
/**
* @see org.springmodules.jcr.JcrCallback#doInJcr(javax.jcr.Session)
*/
public Object doInJcr(Session session) throws
RepositoryException, IOException {
  
       
JcrConstants jcrConstants = JcrConstants(session);
       
//create the file node - see section 6.7.22.6 of the spec
Node fileNode = folderNode.addNode(file.getName,
jcrConstants.getNT_FILE);
 
//create the mandatory child node - jcr:content
Node resNode = fileNode.addNode(jcrConstants.getJCR_CONTENT,
jcrConstants.getNT_RESOURCE);
   
     resNode.Property(jcrConstants.getJCR_MIMETYPE, mimeType);
    resNode.Property(jcrConstants.getJCR_ENCODING, encoding);
resNode.Property(jcrConstants.getJCR_DATA, FileInputStream(file));
Calendar lastModied = Calendar.getInstance;
lastModied.TimeInMillis (file.lastModied );
resNode.Property(jcrConstants.getJCR_LASTMODIFIED, lastModied);
             
session.save;
             
resNode;
}
});
}

主要区别是:代码被包装在个JCR模板中它将我们从不得不使用try/catch语句块(IORepository需检查异常)和处理会话(和事务如果有话)清除工作中解放出来值得提及是硬编码如“jcr:data”是通过JcrConstants工具类解析出来它知道名字空间前缀变化并提供种干净方式处理JCR常数正如你看到我只是使例子更加健壮但是对于实际业务代码影响最小

事务支持

使用JCR模块个好处就是能将Spring事务基础设施(包括声明性和编程性)应用于Java内容仓库JSR 170将事务支持视为可选特性并没有强制个标准方式来暴露事务钩子因此每个实现可以选择区别思路方法在本文撰写时只有Jackrabbit支持事务(在它大部分操作中)它通过为每个JcrSession暴露javax.transaction.XAResource做到这JCR模块提供LocalTransactionManager用于本地事务:

<bean id="jcrTransactionManager" ="org.springmodules.jcr.jackrabbit.LocalTransactionManager">
<property name="sessionFactory" ref="jcrSessionFactory"/>
</bean>


为了声明事务划分我和上述事务管理器bean声明起使用标准Spring类:

<!-- transaction proxy for Jcr services/facades -->

<bean id="txProxyTemplate" abstract="true" ="org.springframework.transaction.erceptor.TransactionProxyFactoryBean">
<property name="proxyTargetClass">
        <value>true</value>
     </property>


     <property name="transactionManager" ref="jcrTransactionManager"/>
     <property name="transactionAttributes">
     <props>
        <prop key="save*">PROPAGATION_REQUIRED</prop>
       <prop key="*">PROPAGATION_REQUIRED, readOnly</prop>
     </props>
     </property>
</bean>

<bean id="jcrService" parent="txProxyTemplate">
    <property name="target">
        <bean ="org.springmodules.examples.jcr.JcrService">
            <property name="template" ref="jcrTemplate"/>
        </bean>
    </property>
</bean>

如果要求个JTA管理器个简单而优雅解决办法是使用来自Jackrabbit捐献包jca连接器为了使用jca你并不需要个应用服务器你可以用个可插入JCA容器如JencksJCA容器配置已经超出本文范围但是你可以参考JCR模块例子中使用Jencks例子

TransactionAwareRepository

对于要求普通JCR代码应用JCR模块允许用直接使用JCR API代码透明地使用事务驱动会话 此时可以使用TransactionAwareRepository它有个参数是JcrSessionFactory这样在使用Session.login(它接收定义在JcrSessionFactory参数)创建任何新会话时如果发现有线程绑定会话就将返回它注意:如果使用事务JCR会话就是事务性否则你必须手动设置属性allowNonTxRepository为true配置如下要不然将抛出个异常:

<bean id="transactionRepository" ="org.springmodules.jcr.TransactionAwareRepository">
     <property name="allowNonTxRepository" value="true"/>
     <property name="targetFactory" ref="jcrSessionFactory"/>
</bean>

transactionRepository bean可以被用作个普通JCR仓库不关心底层机制或线程绑定会话、事务性或非事务性(如果有事务关闭会话时要提交事务)

可选特性支持侦测

为了最大化代码重用但仍然允许插入可选特性如用于区别JCR实现事务支持JCR模块使用SessionHolder接口(前面已经提到)同时还有SessionHolderProviderSessionHolderProviderManager接口用户般不用和它们打交道它们是框架内部使用;但是它们代表了JCR模块主要扩展点

SessionHolder类被内部区别组件使用主要被事务管理器用来操作会话SessionHolderProviderSessionHolderProviderManager处理sessionHolder创建方式以及提供者是如何被个别使用缺省将使用ServiceSessionHolderProviderManager它利用JDK 1.3 Service Provider自动发现特性管理器将在类路径中搜索META-INF/services/org.springmodules.jcr.SessionHolderProvider条目它包含了SessionHolderProvider实现完整限定名Jackrabbit支持就是这样配置JCR模块分发包中包含META-INF/services/org.springmodules.jcr.SessionHolderProvider(译注:原文有误没有给出后面文件名)文件它只有行:

org.springmodules.jcr.jackrabbit.support.JackRabbitSessionHolderProvider


缺省SessionHolderProviderManagerJcrSessionFactory内部使用因此在工厂启动时任何客户化实现可以被获取并和合适仓库起使用但是通过设置JcrSessionFactorySessionHolderProviderManager可以很容易切换到个区别发现策略个可替代发现服务是ListSessionHolderProviderManager它接收组提供者列表可以方便地使用自定义提供(如测试)

<bean id="listProviderManager" ="org.springmodules.jcr.support.ListSessionHolderProviderManager">
     <property name="providers">
         <list>
            <bean ="org.mycompany.jcr.CustomHolderProvider"/>
            <bean ="org.springmodules.jcr.jackrabbit.support.JackRabbitSessionHolderProvider"/>
            <bean ="org.springmodules.jcr.support.GenericHolderProvider"/>
         </list>
     </property
</bean>


<bean id="jcrSessionFactory" ="org.springmodules.jcr.JcrSessionFactory">
        ...
    <property name="sessionHolderProviderManager" ref="listProviderManager"/>
</bean>

注意每个仓库个提供者如果列表包含多个工作于同仓库提供者顺序将非常重要先匹配先使用

Java内容仓库未来

尽管JSR-170已经于2005年5月完成Java内容仓库工作并没有终止JSR-283官方后继者将聚焦于功能增强如联邦remoting客户端/服务器协议映射和扩展内容模型能力同时还存在着些JSR的外想法和项目:绑定/映射框架它可以将java类转换为个JCR树反的亦然(类似ORM后端用Java内容仓库替代数据库)建构于JCR的上WebDAV服务器(参见Jackrabbit捐献包)以及其他已经出现了用于区别产品JSR-170连接器如Alfresco、BEA Portal Server和IBM Domino

至于JCR模块路线图包括用于几个实现Acegi安全集成支持Spring 2.0名字空间模式(它将减少XML配置)和和其它JCR实现集成很显然JCR看起来片光明

TAG: Spring JSR-170

0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: