程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 集成Java內容倉庫和Spring

集成Java內容倉庫和Spring

編輯:關於JAVA

保存各種信息對於應用程序來說非常平常,大多數時候它們是保存在關系數據 庫中。數據庫處理規范數據類型十分在行,但是在處理如圖像、文檔等二進制數 據時卻不是那麼得心應手。盡管可以用文件系統作為替代——而且它們還提供了 更好的性能。但它們既沒有提供用於搜索信息的查詢語言,也沒有提供表示關系 或事務的概念。

在很多情況下,允許第三方訪問這些存儲數據(隨著應用程序的不斷擴展,這 成為一個典型的需求)是一個漫長而復雜的過程(它們不會在一夜之間完成)。 內部存儲結構很容易影響API架構,以及信息檢索與遍歷的方式。

什麼是JSR-170

幸運的是,被稱為Java內容倉庫(Java Content Repository,JCR)的JSR- 170,試圖以獨立於具體實現的方式解決這些(以及其它)問題。即,不論底層資 源(如,數據庫,本地或虛擬文件系統)是什麼,API都將相同。在數據存儲之上 ,JCR提供諸如訪問粒度控制、版本控制、內容事件、全文檢索和過濾等內容服務 。由Day Software領導的JSR-170背後的專家組令人印象深刻,包括內容管理系統 (CMS)提供商Vignette、Hummingbird Ltd.、Stellent和通用Java驅動解決方案 提供商,如BEA Systems、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是必須的,它提供對倉庫的讀訪問,簡而言之:

對節點和屬性的讀訪問。

對屬性值的讀訪問。

輸出到XML/SAX。

支持XPATH語法的查詢服務。

級別 2級別2提供寫功能:

增加和移除節點和屬性。

對屬性值的寫操作。

從XML/SAX輸入數據。

值得注意的是,JCR的實現並不要求達到級別2或者更高層次。因此與只讀倉庫 一起工作也是完全符合規范的。

可選級別“可選”級別包含一些高級特性,它並不是讀寫倉庫所必需的,但確 是真正為JSR-170增色的部分。這個級別包括(除了其它之外):

事務—— 它使倉庫有可能與JMS或JDBC資源一起工作。

版本標定—— 允許倉庫記錄節點的不同狀態,以備日後檢索。規范對於這個 主題有相當的篇幅;該特性使得用JSR-170作為後端構建一個CVS的克隆成為可能 。

事件—— 亦稱觀察,它允許倉庫內發生的任何活動都會被通知給客戶端。

鎖—— 可以凍結部分樹的功能,它可以有效地返回一個只讀的子樹。

API回顧

使用JSR-170時,建議使用來自javax.jcr包的接口。這樣,更換JCR實現時會 容易些,不會有任何代碼的變動。

API的核心類是Session,它代表客戶端和倉庫之間的連接,使用連接活躍其上 的workspace名和所提供的credentials進行定義。Session包含讀(級別1)和寫 (級別2)方法;使用底層倉庫不支持的功能時將拋出異常。

這個包還包含了那些組成倉庫的單元接口的定義:Workspace,Credentials, Node,Property,Item(Node和Property的超類)和Value。javax.jcr.query包 負責處理查詢,javax.jcr.nodetype包負責定義節點類型。剩余的包負責可選級 別的功能,如javax.jcr.version、javax.jcr.observation、javax.jcr.lock。 一個有趣的包是javax.jcr.util,它包含一個ItemVisitor的實現,它源自GOF( 四人幫,Gang of Four)撰寫的著名的設計模式中的訪問者模式(Visitor- pattern)接口。

JSR-170實現

Google和SourceForge會列出好幾頁的JSR-170實現,但是它們中的大多數都處 於alpha階段,沒有發布任何版本。以下是一個可以自由下載的項目列表,它們已 經被作者使用過:

Jackrabbit

它是JSR-170的參考實現,Apache基金的一部分,提供級別1,2和可選功能。 在撰寫本文時,它已經經過孵化階段並有一個官方公開的發布版本,該版本被認 為足夠穩定,可以被用在產品環境。此外,Jackrabbit也被用來作為Day Software(JSR-170的領導者)的商業產品的基礎。除了實現JSR-170中定義的所 有特性,JackRabbit還加入了額外的功能(如SessionListeners或CustomNode注 冊),以及一個有趣的捐獻來的項目套件,它包括:JCA連接器、taglib、WebDAV 接口、虛擬文件系統和JDBC後端。JackRabbit的許可證是Apache 2.0。

eXo JCR

它是eXo platoform的一部分,包含規范要求的所有強制特性和幾個可選特性 。最近一次的版本發布(1.0RC7)是2006年6月22日,基於規范的最終草案2。eXo JCR支持JDBC兼容數據庫,如MySQL、DB2或HSQL(它是缺省的)作為後端存儲,它 是雙許可的(GPL和商用),最終版的發布日期尚未確定。

Jeceira

與Jackrabbit和eXo JCR相比,它是相對較新的項目。它實現了級別1和2的一 些需求,只在寫操作時,支持來自可選級別的觀察功能。不幸的是,這個項目處 於未完成階段,在過去的9個月沒有新版本發布。然而它被Magnolia(一個流行的 基於java的CMS,與作為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和Jeceira的FactoryBean,以及一個可以很容易支持其它倉 庫的抽象基類。

SessionFactory,用來統一Repository,Credentials和Workspace接口,允許 自動注冊監聽器和自定義名字空間。

Spring聲明性事務為那些實現了(可選)事務特性的倉庫提供了支持。

OpenSessionInView攔截器和過濾器允許每個線程跨不同組件使用同一會話。 與JcrTemplate一起,檢索、關閉和管理JCR會話的工作已經外部化,對於調用者 完全透明。

本文將使用參考實現(Jackrabbit),由於JCR模塊使用的是javax.jcr接口, 因此改變實現根本就是一個配置的問題。讓我們一步一步地來看看在Jackrabbit 之上如何使用Java內容倉庫,以及如何讓Spring模塊來幫助完成這一工作。

配置倉庫和SessionFactory<bean id="repository" class="org.springmodules.jcr.jackrabbit.RepositoryFactoryBean">
  <!-- normal factory beans params -->
  <property name="configuration" value="classpath:jackrabbit- repo.xml"/>
  <property name="homeDir" ref="./tmp/repo"/>
</bean>

JCR支持提供RepositoryFactoryBean類配置Jackrabbit,它需要JackRabbit的 配置文件和主目錄。注意,RepositoryFactoryBean在使用本地文件系統時特別有 用;對於服務器環境,倉庫可能被注冊在JNDI中,此時可以使用 JndiObjectFactoryBean幫助類(Spring分發包的一部分)檢索它:

<bean id="repository" class="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 interface 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" class="org.springmodules.jcr.JcrSessionFactory">
<property name="repository" ref="repository"/>
<property name="credentials">
<bean class="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 return it as a char[] -->
<bean id="password" class="java.lang.String">
<constructor-arg index="0" value="pass"/>
</bean>

這個bean聲明非常簡單,唯一需要注意的地方是,密碼被提供給 SimpleCredential的構造函數:它只接受字符數組,使用Spring工廠聲明作為一 種變通。

JcrTemplate

JcrTemplate是JCR模塊的核心類之一,它提供了與JCR會話一起工作的方便方 法,將調用者從必須處理的打開和關閉會話、事務回滾(如果底層倉庫提供)、 以及處理其它特性中的異常等工作中解放出來:

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

模板定義非常簡單,類似來自Spring框架的其它模板類,如 HibernateTemplate。

例子

既然倉庫已經配置了,接下來看看“Spring化”的例子之一,它來自 Jackrabbit的wiki頁:

public Node importFile(final Node folderNode, final File file, final String mimeType,
   final String encoding) {
  return (Node) execute(new JcrCallback() {

   /**
   * @see org.springmodules.jcr.JcrCallback#doInJcr (javax.jcr.Session)
   */
   public Object doInJcr(Session session) throws
   RepositoryException, IOException {

   
   JcrConstants jcrConstants = new 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.setProperty(jcrConstants.getJCR_MIMETYPE(), mimeType);
     resNode.setProperty(jcrConstants.getJCR_ENCODING(), encoding);
   resNode.setProperty(jcrConstants.getJCR_DATA(), new FileInputStream(file));
   Calendar lastModified = Calendar.getInstance();
   lastModified.setTimeInMillis (file.lastModified ());
   resNode.setProperty(jcrConstants.getJCR_LASTMODIFIED(), lastModified);

   session.save();

   return resNode;
   }
  });
}

主要區別是:代碼被包裝在一個JCR模板中,它將我們從不得不使用的 try/catch語句塊(因為IO和Repository的需檢查異常)和處理會話(和事務,如 果有的話)清除工作中解放出來。值得提及的是硬編碼字符串,如“jcr:data” ,是通過JcrConstants工具類解析出來的。它知道名字空間的前綴變化,並提供 一種干淨的方式處理JCR常數。正如你看到的,我只是使例子更加健壯,但是對於 實際業務代碼影響最小。

事務支持

使用JCR模塊的一個好處就是能將Spring事務基礎設施(包括聲明性和編程性 )應用於Java內容倉庫。JSR 170將事務支持視為可選特性,並沒有強制一個標准 的方式來暴露事務鉤子,因此每個實現可以選擇不同的方法。在本文撰寫時,只 有Jackrabbit支持事務(在它的大部分操作中),它通過為每個JcrSession暴露 一個javax.transaction.XAResource做到這一點。JCR模塊提供 LocalTransactionManager用於本地事務:

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

為了聲明事務劃分,我與上述事務管理器bean聲明一起使用標准Spring類:

<!-- transaction proxy for Jcr services/facades -->
<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFact oryBean">
   <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 class="org.springmodules.examples.jcr.JcrService">
       <property name="template" ref="jcrTemplate"/>
     </bean>
   </property>
</bean>

如果要求一個JTA管理器,一個簡單而優雅的解決辦法是使用來自Jackrabbit 捐獻包的jca連接器。為了使用jca,你並不需要一個應用服務器,因為你可以用 一個可插入的JCA容器,如Jencks。JCA容器的配置已經超出本文的范圍,但是你 可以參考JCR模塊例子中使用Jencks的例子。

TransactionAwareRepository

對於要求普通JCR代碼的應用程序,JCR模塊允許用直接使用JCR API的代碼, 透明地使用事務驅動會話。 此時,可以使用TransactionAwareRepository,它有 一個參數是JcrSessionFactory。這樣,在使用Session.login()(它接收定義在 JcrSessionFactory中的參數)創建任何新會話時,如果發現有線程綁定的會話, 就將返回它。注意:如果使用事務,JCR會話就是事務性的,否則你必須手動設置 屬性allowNonTxRepository為true,配置如下,要不然將拋出一個異常:

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

transactionRepository bean可以被用作一個普通的JCR倉庫,不關心底層機 制或線程綁定會話、事務性或非事務性(如果有事務,關閉會話時要提交事務) 。

可選特性支持偵測

為了最大化代碼重用,但仍然允許插入可選特性,如用於不同JCR實現的事務 支持,JCR模塊使用SessionHolder接口(前面已經提到),同時還有 SessionHolderProvider和SessionHolderProviderManager接口。用戶一般不用與 它們打交道,因為它們是框架內部使用的;但是,它們代表了JCR模塊主要的擴展 點。

SessionHolder類被內部不同組件使用,主要被事務管理器用來操作會話, SessionHolderProvider和SessionHolderProviderManager處理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.JackRabbitSessionHold erProvider

缺省,SessionHolderProviderManager被JcrSessionFactory內部使用,因此 在工廠啟動時,任何客戶化實現可以被獲取,並與合適的倉庫一起使用。但是, 通過設置JcrSessionFactory中的SessionHolderProviderManager,可以很容易的 切換到一個不同的發現策略。一個可替代的發現服務是 ListSessionHolderProviderManager,它接收一組提供者列表,可以方便地使用 自定義提供(如測試)。

<bean id="listProviderManager" class="org.springmodules.jcr.support.ListSessionHolderProviderManager"& gt;
   <property name="providers">
     <list>
       <bean class="org.mycompany.jcr.CustomHolderProvider"/>
       <bean class="org.springmodules.jcr.jackrabbit.support.JackRabbitSessionHolder Provider"/>
       <bean class="org.springmodules.jcr.support.GenericHolderProvider"/>
     </list>
   </property
</bean>
<bean id="jcrSessionFactory" class="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的看起來 一片光明。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved