gembin

          OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

          HBase, Hadoop, ZooKeeper, Cassandra

          Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

          There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

          About Me

           

          Java Content Repository API 簡介

          隨著內(nèi)容管理應(yīng)用程序的日益普及,對于公共的、標準的內(nèi)容倉庫 API 的需求也變得漸漸明顯起來。Content Repository for Java ™ Technology API(JSR-170)的目標就是提供這樣一個接口。在這篇文章中,我將用開放源碼的 JSR-170 實現(xiàn) Apache Jackrabbit,設(shè)計一個簡單的類似維京百科全書的后端,研究這個前途遠大的框架所提供的特性。

          如果曾經(jīng)試過開發(fā)內(nèi)容管理應(yīng)用程序,那么您應(yīng) 當(dāng)非常清楚在實現(xiàn)內(nèi)容系統(tǒng)時所遇到的固有難題。這個領(lǐng)地有點支離破碎,許多供應(yīng)商都有自己的私有倉庫引擎。這些困難惡化了這類系統(tǒng)的復(fù)雜性和可維護性、增 強了廠商鎖定、增加了企業(yè)市場中對傳統(tǒng)系統(tǒng)長期支持的需要。隨著企業(yè) weblog 和電子企業(yè)文檔管理的日益流行,對標準化內(nèi)容倉庫接口的需求比以往任何時候都更加顯著。

          Content Repository for Java Technology 規(guī)范是在 Java Community Process 中作為 JSR-170 開發(fā)的,它的目標是滿足這些行業(yè)的需求。該規(guī)范在 javax.jcr 名稱空間中提供了統(tǒng)一的 API ,允許以廠商中立的方式訪問任何符合規(guī)范的倉庫實現(xiàn)。

          但是 API 標準化并不是 Java Content Repository(JCR)帶來的惟一特性。JSR-170 的一個主要優(yōu)勢就是沒有捆綁到任何特定的底層架構(gòu)上。例如,JSR-170 實現(xiàn)的后端數(shù)據(jù)存儲可以是文件系統(tǒng)、WebDAV 倉庫、XML 支持的系統(tǒng)或者是 SQL 數(shù)據(jù)庫。而且,JSR-170 的導(dǎo)出和導(dǎo)入功能允許集成人員在后端內(nèi)容和 JCR 實現(xiàn)之間無縫地切換。最后,JCR 提供了簡單的接口,可以將該接口放在各種現(xiàn)有的內(nèi)容倉庫之上,并同時標準化一些復(fù)雜的功能(例如版本管理、訪問控制和搜索)。

          在討論 JCR 時,有幾種方式可以采用。在這篇文章中,我從開發(fā)人員的角度來研究 JSR-170 規(guī)范所提供的特性,重點放在可用的 API 和接口上,這些接口允許程序員在設(shè)計內(nèi)容應(yīng)用程序時有效利用 JSR-170 倉庫。作為一個假設(shè)的示例,我將為一個類似維京百科全書的、叫做 JCRWiki 的系統(tǒng)實現(xiàn)一個小小的后端,為二進制內(nèi)容、版本管理、備份和搜索提供支持。我使用 Apache Jackrabbit(JSR-170 的開源實現(xiàn))開發(fā)這個應(yīng)用程序。

          倉庫模型

          我先從對倉庫模型的高級討論開始,以便讓您熟悉 JCR。倉庫模型是簡單的層次結(jié)構(gòu),看起來就像一個有 n 個分叉的樹。它由單一內(nèi)容倉庫構(gòu)成,有一個或多個工作區(qū)。(這篇文章中的討論僅限制于單一工作區(qū)。)每個工作區(qū)都包含一個項目 樹;項目既可以是節(jié)點 也可以是屬性。節(jié)點可以有零個或多個子節(jié)點以及零個或多個相關(guān)屬性,實際的內(nèi)容保存在子節(jié)點和屬性中。

          每個節(jié)點都有且只有一個主節(jié)點類型。主節(jié)點類型定義了節(jié)點的特征,例如允許節(jié)點擁有的屬性和子節(jié)點。除了主節(jié)點類型之外,節(jié)點還可以有一個或多個混合(mixin)類型。混合類型更像修飾器,向節(jié)點提供額外的特征。具體來說,JCR 實現(xiàn)可以提供三種預(yù)定義混合類型:

          • mix:versionable:允許節(jié)點支持版本管理
          • mix:lockable:支持節(jié)點的鎖定功能
          • mix:referenceable:提供自動創(chuàng)建的 jcr:uuid 屬性,給節(jié)點一個惟一可以引用的標識符

          這個結(jié)構(gòu)如圖 1 所示。圓圈代表節(jié)點,矩形代表屬性。請參見節(jié)點 A、B 和 C,它們都衍生自一個根節(jié)點。節(jié)點 A 有兩個屬性,即一個字符串 “John” 和一個整數(shù) 22。


          圖 1. 有多個工作區(qū)的倉庫模型
          有多個工作區(qū)的倉庫模型

          預(yù)定義的節(jié)點類型

          每個倉庫都必須支持主節(jié)點類型 nt:base。倉庫還可以支持其他許多公共節(jié)點類型:

          • nt:unstructured 是最靈活的節(jié)點類型。它允許使用任意數(shù)量的子節(jié)點或?qū)傩裕⑶铱梢允褂萌我饷Q。這個節(jié)點類型表示 JCRWiki 的條目。

          • nt:file 表示文件。它需要一個叫做 jcr:content 的單一子節(jié)點。這個節(jié)點類型表示 JCRWiki 條目中的圖片和其他二進制內(nèi)容。

          • nt:folder 節(jié)點類型可以表示文件夾,就像常規(guī)的文件系統(tǒng)中的文件夾一樣。

          • nt:resource 通常表示文件的實際內(nèi)容。

          • nt:version 是支持版本管理的倉庫所必需的節(jié)點類型。

          整個節(jié)點類型的結(jié)構(gòu)可以在 JSR-170 規(guī)范的 6.7.22.1 小節(jié)找到(請參閱 參考資料 獲得鏈接)。

          名稱空間

          倉庫模型一個有用的卻經(jīng)常被忽視的特性就是它對名稱空間 的支持。名稱空間防止不同來源和不同應(yīng)用程序域之間的項目和節(jié)點類型的命名沖突。名稱空間被定義為帶有一個前綴,中間用一個 : (冒號)分隔。在這篇文章的教程中,已經(jīng)遇到了一些名稱空間:jcr 用于 JCR 的內(nèi)部屬性,mix 用于混合類型,nt 用于節(jié)點類型。在 JCRWiki 中,所有的數(shù)據(jù)都將使用 wiki 名稱空間。

          安裝 JCR

          在編寫這篇文章的時候,Apache Jackrabbit(即 Apache 基金會的 JSR-170 的開源實現(xiàn))的發(fā)行版已經(jīng)到了版本 1.0。編譯好的字節(jié)碼 JAR 可以直接從 Jackrabbit Web 站點下載(請參閱 參考資料)。雖然 Jackrabbit can 仍然可以用 SVN 從源代碼進行編譯,但是 Jackrabbit 庫已經(jīng)非常穩(wěn)定,不再需要每夜構(gòu)建(nightly builds)技術(shù)。這一節(jié)將提供盡可能快地安裝 JCR 實現(xiàn)并運行它的詳細說明。

          需要的庫

          要使用和運行這篇文章中的示例,請將下面這些庫放在類路徑中:

          • jackrabbit-core:針對 JSR-170 的 Jackrabbit 內(nèi)容倉庫核心實現(xiàn)和來自 Apache 的公共實用代碼。

          • commons-collections:包含強大數(shù)據(jù)結(jié)構(gòu)的框架,該框架可以加快 Java 應(yīng)用程序的開發(fā)。

          • concurrent:這個庫提供通常在 Java 并發(fā)編程中會遇到的工具類的標準化的、有效率的版本。

          • derby:一個 Apache 數(shù)據(jù)庫子項目,它提供完全用 Java 語言實現(xiàn)的關(guān)系數(shù)據(jù)庫。

          • jcr:一組符合 JSR-170 規(guī)范的接口。

          • log4j:運行時日志庫。

          • lucene:高性能的全功能文本搜索引擎庫。

          • slf4j (針對 Java 的簡單日志 Facade):目的是充當(dāng)不同日志 API 的簡單 facade,允許用戶在部署時插入需要的實現(xiàn)。

          • xerces:高級 XML 解析器,支持 SAX 版本 2、DOM 1 級和 SAX 版本 1 API。

          如果用 SVN 構(gòu)建 Jackrabbit,那么所有這些 JAR 文件都會在 Jackrabbit 構(gòu)建過程中被下載,并位于 Maven 的緩存目錄中。在 Linux 下,這些 JAR 位于主目錄的 .maven 目錄下。如果使用二進制構(gòu)建,那么只需要從它們各自的 Web 站點下載其二進制版或瀏覽 Jackrabbit Web 站點的 “First Hops with Jackrabbit” 即可,那里會提供到所有這些資源的直接鏈接。在 JSR-170 規(guī)范的下載中還有一個 jcr-1.0.jar,在 Java 社區(qū)進程的 Web 站點上也可以找到它。

          手工配置

          JSR-170 沒有確切地指定應(yīng)當(dāng)如何獲得初始的 Repository 對象;這被留作每個倉庫廠商的實現(xiàn)細節(jié)。但是,在應(yīng)用程序中最好使用 JNDI 或其他容器環(huán)境中的配置機制,這樣可以保持 JSR-170 的實現(xiàn)相對獨立于對 Jackrabbit 的直接依賴項。雖然這一策略在初始配置期間造成了額外的復(fù)雜性,但它提供了跨不同 JSR-170 實現(xiàn)的更好的移植性。要想獲得一個移植性雖然差但得到了簡化的配置,可以使用自動配置,詳細內(nèi)容在這篇文章后面部分介紹。

          在手工配置中,可以將 JNDI 與配置文件(叫做 repository.xml,以編程方式載入)結(jié)合使用來得到倉庫。

          倉庫配置

          第一步,也是最容易的一步,就是為 Jackrabbit 創(chuàng)建 repository.xml 文件。這個配置文件實現(xiàn)了許多重要任務(wù)。這些任務(wù)包括:指定底層的后端存儲、訪問控制機制、可用的工作區(qū)、版本管理系統(tǒng)和搜索子系統(tǒng)。清單 1 提供了一個示例:


          清單 1. 示例 repository.xml 配置文件
          <?xml version="1.0" encoding="ISO-8859-1"?>
          <Repository>
          <FileSystem
          class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
          <param name="path" value="${rep.home}/repository"/>
          </FileSystem>
          <Security appName="Jackrabbit">
          <AccessManager
          class="org.apache.jackrabbit.core.security.
          SimpleAccessManager"/>
          </Security>
          <Workspaces
          rootPath="${rep.home}/workspaces"
          defaultWorkspace="default" />
          <Workspace name="${wsp.name}">
          <FileSystem
          class="org.apache.jackrabbit.core.fs.local.
          LocalFileSystem">
          <param name="path" value="${wsp.home}"/>
          </FileSystem>
          <PersistenceManager
          class="org.apache.jackrabbit.core.state.xml.
          XMLPersistenceManager" />
          <SearchIndex
          class="org.apache.jackrabbit.core.query.lucene.
          SearchIndex">
          <param name="path" value="${wsp.home}/index" />
          </SearchIndex>
          </Workspace>
          <Versioning rootPath="${rep.home}/versions">
          <FileSystem
          class="org.apache.jackrabbit.core.fs.local.
          LocalFileSystem">
          <param name="path" value="${rep.home}/versions"/>
          </FileSystem>
          <PersistenceManager
          class="org.apache.jackrabbit.core.state.xml.
          XMLPersistenceManager" />
          </Versioning>
          </Repository>

          這個配置使用本地文件系統(tǒng)來保存?zhèn)}庫數(shù)據(jù),用 SimpleAccessManager 進行訪問控制。文件剩下的部分基本是自解釋的,將它們原樣復(fù)制到自己的倉庫目錄中即可。

          安全性配置

          使用 JASS 配置文件 jaas.config(存放在項目的根目錄中),可以提供初步的安全性。清單 2 提供了一個示例:


          清單 2. 示例 JAAS 配置
          Jackrabbit {
          org.apache.jackrabbit.core.security.SimpleLoginModule required
          anonymousId="anonymous";
          };

          倉庫初始化代碼

          清單 3 描述的包可在初始化倉庫時使用:


          清單 3. 手工配置的初始化 import 語句
          import org.apache.jackrabbit.core.jndi.RegistryHelper;

          import javax.naming.Context;
          import javax.naming.InitialContext;
          import javax.naming.NamingException;

          import javax.jcr.*;
          import javax.jcr.query.*;
          import javax.jcr.version.*;

          import java.util.Hashtable;
          import java.util.Calendar;
          import java.io.*;

          import sun.net.www.MimeTable;

          要得到 Repository 對象,請將 configFile 變量設(shè)置成指向 repository.xml 文件,將 repHomeDir 變量設(shè)置成指向倉庫所在的本地文件系統(tǒng)目錄。當(dāng)結(jié)合 RegistryHelper 使用 JNDI 時,獲得倉庫非常簡單,如清單 4 所示:


          清單 4. 用 JNDI 獲得 repository 對象
          String configFile = "repository.xml";
          String repHomeDir = "repository";

          Hashtable env = new Hashtable();
          env.put(Context.INITIAL_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 r = (Repository) ctx.lookup("repo");

          接下來,用 SimpleCredentials 獲得 Session 對象。在這個實現(xiàn)中,SimpleCredentials 接受所有用戶名。替代的 JCR 實現(xiàn)可以提供更復(fù)雜的認證機制,可以連接到 LDAP 服務(wù)器或外部數(shù)據(jù)庫來提供憑據(jù)信息。(身份驗證和訪問控制的完整功能超出了本文的范圍。要獲得有關(guān)的更多信息,請參閱 JSR-170 規(guī)范的 6.9 小節(jié)。)

          Session 對象為程序員提供了一個臨時的存儲層,它非常像傳統(tǒng)的對象關(guān)系映射工具中可以看到的層,而且還可以將它看作到特定工作區(qū)的連接。它允許客戶訪問綁定到這個 會話的任何節(jié)點或?qū)傩浴Mㄟ^會話,可以得到工作區(qū),再從工作區(qū)得到根節(jié)點。所有這些步驟都是在清單 5 的簡短代碼片段中完成的:


          清單 5. 獲得工作區(qū)和根節(jié)點
          SimpleCredentials cred = new SimpleCredentials("userid",
          "".toCharArray());

          Session session = r.login(cred, null);
          Workspace ws = session.getWorkspace();
          Node rn = session.getRootNode();

          使用會話、工作區(qū)和根節(jié)點引用,現(xiàn)在可以通過不同的抽象層訪問倉庫的特性。最后,為了驗證倉庫已經(jīng)成功獲得初始化,可以用 rn.getPrimaryNodeType().getName() 輸出根節(jié)點的名稱。這應(yīng)當(dāng)形成以下輸出:

          rep:root

          因為正在使用 JAAS,所以請記得將 -Djava.security.auth.login.config==jaas.config. 以 Java JVM 參數(shù)的形式包含進來。

          JCRWiki 名稱空間

          在這個練習(xí)中,所有的 JCRWiki 內(nèi)容都放在 wiki 名稱空間下。為了讓倉庫識別這個名稱空間,必須在初始化時注冊名稱空間,如下所示:

          ws.getNamespaceRegistry()。registerNamespace
          ("wiki", "http://www.barik.net/wiki/1.0");

          恭喜!倉庫的手工配置現(xiàn)在完成了。



          自動配置

          Jackrabbit 實現(xiàn)還提供了一個 TransientRepository 類,這個類來自其核心 API,可以在啟動第一個會話時自動初始化內(nèi)容倉庫,并在最后一個會話關(guān)閉時停止使用倉庫。對于簡單的獨立應(yīng)用程序,使用 TransientRepository 可以極大地簡化倉庫的配置,但要以 JSR-170 的移植性作為代價。

          TransientRepository 自動創(chuàng)建 repository.xml 和倉庫文件夾。它還在內(nèi)部提供了處理身份驗證和安全性的 SimpleAccessManager

          自動配置需要使用如圖 6 所示的初始化 import 語句。與手工配置相比,所有的 JNDI 引用都被刪除了。在 RegistryHelper 的位置換上了 TransientRepository


          清單 6. 自動配置的 import 語句
          import org.apache.jackrabbit.core.TransientRepository

          import javax.jcr.*;
          import javax.jcr.query.*;
          import javax.jcr.version.*;

          import java.util.Calendar;
          import java.io.*;

          import sun.net.www.MimeTable;

          因為 TransientRepository 為您執(zhí)行了初始化,所以獲得倉庫非常簡單,如清單 7 所示:


          清單 7. 用 TransientRepository 獲得倉庫、工作區(qū)和根節(jié)點
          Repository r = new TransientRepository();
          Session session = r.login(new SimpleCredentials("userid", "".toCharArray()));

          Workspace ws = session.getWorkspace();
          Node rn = session.getRootNode();

          像手工配置時一樣,所有的 JCRWiki 內(nèi)容都放在 wiki 名稱空間下:

          ws.getNamespaceRegistry()。registerNamespace

          JCRWiki 的設(shè)計策略

          現(xiàn)在看一下 JCRWiki 倉庫的整體內(nèi)容層次結(jié)構(gòu)。在示例中,要創(chuàng)建兩個實體 “rose” 和 “Shakespeare”,它們都是 nt:unstructured 類型的。根據(jù)設(shè)計合同,每個百科全書條目都要有三個屬性:條目的標題、條目的內(nèi)容以及多值分類屬性(如果條目有多個分類)或單值分類屬性(如果條目只有一個分類)。多值屬性在編程上表現(xiàn)為一組數(shù)值。

          圖 2 描繪了 JCRWiki 設(shè)計策略的圖示:


          圖 2. JCRWiki 拓撲的高層圖示
          JCRWiki 拓撲的高層圖示

          JCRWiki 功能

          沒有內(nèi)容的倉庫沒什么用處。這一節(jié)將演示 JSR-170 提供的基本內(nèi)容操縱功能,并描述一些更高級的、可選的倉庫特性,例如版本管理和導(dǎo)入導(dǎo)出 XML 內(nèi)容。

          添加內(nèi)容

          從清單 8 開始,向倉庫添加內(nèi)容節(jié)點,讓它看起來像圖 2 中的 JCRWiki 拓撲:


          清單 8. 將內(nèi)容添加到 JCR 倉庫中
          Node encyclopedia = rn.addNode("wiki:encyclopedia");

          Node p = encyclopedia.addNode("wiki:entry");
          p.setProperty("wiki:title", new StringValue("rose"));
          p.setProperty("wiki:content", new
          StringValue("A rose is a flowering shrub."));
          p.setProperty("wiki:category",
          new Value[]{
          new StringValue("flower"),
          new StringValue("plant"),
          new StringValue("rose")});

          Node n = encyclopedia.addNode("wiki:entry");
          n.setProperty("wiki:title", new StringValue("Shakespeare"));
          n.setProperty("wiki:content", new
          StringValue("A famous poet who likes roses."));
          n.setProperty("wiki:category", new StringValue("poet"));

          session.save();

          默認情況下,Jackrabbit 的節(jié)點被設(shè)置為 nt:unstructured。注意,“rose” 的分類屬性是多值的。上面代碼段的最后一行代碼將保存會話。添加和設(shè)置節(jié)點以及節(jié)點屬性只能修改臨時的會話存儲層。要將這些變化保持到倉庫中,則必須用 session.save() 保存會話。可以在目標節(jié)點上調(diào)用 Node.remove() 來刪除節(jié)點。

          存取內(nèi)容

          JSR-170 提供了兩種存取內(nèi)容的方法:遍歷 存取和直接 存取。遍歷存取包括用相對路徑在內(nèi)容樹中進行遍歷,直接存取允許用絕對路徑直接跳到節(jié)點,如果節(jié)點是可以引用的,則用 UUID 直接跳到節(jié)點。因為兩種存取之間存在相似性,所以我在這篇文章只側(cè)重于遍歷存取。

          從任何 Node 對象及其方法 Node.getNode()Node.getProperty() 都可以進行遍歷存取。通過使用 JCRWiki 拓撲,可以用以下代碼從根節(jié)點獲得 encyclopedia 節(jié)點:

          Node encyclopedia = rn.getNode("wiki:encyclopedia");

          可以進一步通過遍歷得到屬性。例如,在根節(jié)點向下的 “rose” 百科全書節(jié)點條目中,假設(shè)以前知道 JCRWiki 拓撲,那么可以像這樣通過遍歷得到屬性:

          String roseTitle = rn.getProperty 
          ("wiki:encyclopedia/wiki:entry[1]/wiki:title").getString()

          請注意,您是通過 wiki:entry[1] 進行遍歷的。當(dāng)有同名的多個同級節(jié)點時,可以用下標區(qū)分出想要的同級節(jié)點。在 JCR 中,對同級節(jié)點的索引是從 1 而不是 0 開始的。而且,索引的順序是在通過 Node.getNodes() 得到的迭代器中返回的節(jié)點的順序。

          然后,可以通過獲取 NodeIterator(它返回特點節(jié)點的子節(jié)點)來瀏覽所有 JCRWiki 條目,如清單 9 所示:


          清單 9. 瀏覽內(nèi)容倉庫
          Node encyclopedia = rn.getNode("wiki:encyclopedia");
          NodeIterator entries = encyclopedia.getNodes("wiki:entry");

          while (entries.hasNext()) {

          Node entry = entries.nextNode();

          System.out.println(entry.getName());
          System.out.println(entry.getProperty("wiki:title")。getString());
          System.out.println(entry.getProperty("wiki:content")。getString());
          System.out.println(entry.getPath());

          Property category = entry.getProperty("wiki:category");

          try {
          String c = category.getValue()。getString();
          System.out.println("Category: " + c);
          } catch (ValueFormatException e) {

          Value[] categories = category.getValues();

          for (Value c : categories) {
          System.out.println("Category: " + c.getString());
          }
          }
          }

          因為分類屬性可以是多值的也可以是單值的,所以要用 try-catch 語句檢查它。如果對多值屬性調(diào)用 getValue(),就會拋出 ValueFormatException。一般來說,直接存取和遍歷存取都需要知道內(nèi)部節(jié)點的結(jié)構(gòu)。所以讓我們來看一種更具表現(xiàn)力的存取節(jié)點的方式:使用搜索。

          用 XPath 搜索內(nèi)容

          正如已經(jīng)看到的,遍歷存取和直接存取都需要知道圖書的位置。獲得特定條目的更好方式是通過 JCR 的 XPath 搜索工具。因為從樹形結(jié)構(gòu)來看,工作區(qū)模型非常類似于 XML 文檔,所以 XPath 是查找節(jié)點的理想語法。XPath 查詢是通過 QueryManager 對象執(zhí)行的。查詢的過程與通過 JDBC 存取記錄類似,如清單 10 所示:


          清單 10. 用 XPath 搜索內(nèi)容
          QueryManager qm = ws.getQueryManager();
          Query q = qm.createQuery
          ("http://wiki:encyclopedia/wiki:entry[@wiki:title = 'rose']",
          Query.XPATH);

          QueryResult result = q.execute();
          NodeIterator it = result.getNodes();

          while (it.hasNext()) {
          Node n = it.nextNode();

          System.out.println(n.getName());
          System.out.println(n.getProperty("wiki:title").getString());
          System.out.println(n.getProperty("wiki:content").getString());
          }

          createQuery() 的第二個參數(shù)指定所使用的查詢語言。JRC 實現(xiàn)可以另外選擇為 SQL 語法支持 Query.SQL。也可以執(zhí)行更復(fù)雜的查詢。例如,可以查詢的內(nèi)容中包含單詞 rose 的所有條目:

          Query q = qm.createQuery
          ("http://wiki:encyclopedia/" +
          "wiki:entry[jcr:contains(@wiki:content, 'rose')]",
          Query.XPATH);

          用 XML 導(dǎo)入和導(dǎo)出內(nèi)容

          JSR-170 為了確保跨 JCR 實現(xiàn)的移植性已經(jīng)做了許多工作。它促進移植性的方式之一就是使用標準的 XML 導(dǎo)入和導(dǎo)出特性。通過使用這些工具,符合規(guī)范的供應(yīng)商倉庫內(nèi)容可以很容易地轉(zhuǎn)移到另一個符合規(guī)范的供應(yīng)商倉庫。使用 XML 進行序列化的另一個優(yōu)勢是:可以用傳統(tǒng)的 XML 解析工具操縱導(dǎo)出的倉庫。只要用清單 11 的三行代碼就可以執(zhí)行導(dǎo)出:


          清單 11. 導(dǎo)出數(shù)據(jù)
          File outputFile = new File("systemview.xml");
          FileOutputStream out = new FileOutputStream(outputFile);
          session.exportSystemView("/wiki:encyclopedia", out, false, false);

          然后可以把生成的 XML 文件轉(zhuǎn)移給另一個新倉庫,如清單 12 所示:


          清單 12. 轉(zhuǎn)移數(shù)據(jù)
          File inputFile = new File("systemview.xml");
          FileInputStream in = new FileInputStream(inputFile);
          session.importXML
          ("/", in, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
          session.save();

          添加二進制內(nèi)容

          直到現(xiàn)在,一直都是用 StringValue 表示屬性和節(jié)點。但是 JCR 還支持其他類型,包括布爾型和長整型。清單 13 演示了 JCR 中可使用的流類型,可在節(jié)點中保存二進制圖片。在這個清單中,可將文件 rose.gif 作為元數(shù)據(jù)添加到 nt:file 節(jié)點中。文件數(shù)據(jù)本身被保存為 nt:resource 子節(jié)點。


          清單 13. 添加二進制內(nèi)容
          File file = new File("rose.gif");
          MimeTable mt = MimeTable.getDefaultTable();
          String mimeType = mt.getContentTypeFor(file.getName());
          if (mimeType == null) mimeType = "application/octet-stream";

          Node fileNode = roseMode.addNode(file.getName(), "nt:file");
          Node resNode = fileNode.addNode("jcr:content", "nt:resource");
          resNode.setProperty("jcr:mimeType", mimeType);
          resNode.setProperty("jcr:encoding", "");
          resNode.setProperty("jcr:data", new FileInputStream(file));
          Calendar lastModified = Calendar.getInstance();
          lastModified.setTimeInMillis(file.lastModified());
          resNode.setProperty("jcr:lastModified", lastModified);

          在使用 MimeTable 類確定了內(nèi)容類型之后,用 FileInputStream 裝入文件。這個問題很簡單,只要給 nt:resource 節(jié)點類型添加命名正確的屬性即可,屬性包含實際的文件數(shù)據(jù)。

          版本管理

          JSR-170 支持許多可選特性,包括訪問控制、事務(wù)、鎖定和版本管理。這些特性本身都可以是個完整的主題,所以我必須簡要地總結(jié)一下,只介紹它們當(dāng)中最流行的那一個:版本管理。在最簡單的情況下,只需將 mix:versionable 混合類型添加到任何節(jié)點,就可以執(zhí)行版本管理。在節(jié)點上,可以用一組類似 CVS 操作的方法實現(xiàn)版本管理,如清單 14 所示:


          清單 14. 版本管理方法
          n.checkout();
          n.setProperty("wiki:content", "Updated content for the entry.");
          n.save();
          n.checkin();

          JCR 中的其他操作包括:更新、合并和恢復(fù)以前版本。要瀏覽指定節(jié)點的整個版本歷史,可以通過清單 15 中的步驟進行:


          清單 15. 瀏覽版本歷史
          VersionHistory vh = n.getVersionHistory();
          VersionIterator vi = vh.getAllVersions();

          vi.skip(1);
          while (vi.hasNext()) {
          Version v = vi.nextVersion();
          NodeIterator ni = v.getNodes();

          while (ni.hasNext()) {
          Node nv = ni.nextNode();
          System.out.println("Version: " +
          v.getCreated()。getTime());

          System.out.println(nv.getProperty("wiki:title").getString());
          System.out.println(nv.getProperty("wiki:content").getString());
          }

          }

          使用 vi.skip(1) 最初看起來可能有點怪,但是如果看到了版本歷史的內(nèi)部保存機制,就應(yīng)該很清楚這種用法了,版本歷史的內(nèi)部保存機制如圖 3 所示:


          圖 3. 節(jié)點版本歷史的結(jié)構(gòu)化模型
          節(jié)點版本歷史的結(jié)構(gòu)化模型

          在節(jié)點的版本歷史中,每個版本都保存為版本歷史的一個子版本,而且包含指向后續(xù)版本的引用。在圖 3 中,版本 Vb 是版本 Va 的后續(xù),版本 Vc 和 Va 是 Vroot 的后續(xù),Vroot 是版本圖的開始。Vroot 是自動創(chuàng)建的子節(jié)點,是版本圖的起點,它不包含任何狀態(tài)信息。所以,當(dāng)應(yīng)用程序在版本歷史中遍歷時,Vroot 被跳過。



          ("wiki", "http://www.barik.net/wiki/1.0");

          恭喜!倉庫的自動配置現(xiàn)在完成了。

          posted on 2008-07-21 13:36 gembin 閱讀(580) 評論(0)  編輯  收藏 所屬分類: JavaSE

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(6)

          隨筆分類(440)

          隨筆檔案(378)

          文章檔案(6)

          新聞檔案(1)

          相冊

          收藏夾(9)

          Adobe

          Android

          AS3

          Blog-Links

          Build

          Design Pattern

          Eclipse

          Favorite Links

          Flickr

          Game Dev

          HBase

          Identity Management

          IT resources

          JEE

          Language

          OpenID

          OSGi

          SOA

          Version Control

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          free counters
          主站蜘蛛池模板: 黄山市| 商都县| 七台河市| 阿合奇县| 禹州市| 神池县| 图们市| 天祝| 永川市| 蒙山县| 吉木萨尔县| 宣武区| 微山县| 教育| 金寨县| 西畴县| 海盐县| 宾川县| 儋州市| 广西| 青海省| 本溪| 碌曲县| 西和县| 伊宁市| 仙桃市| 浠水县| 宽城| 宜黄县| 融水| 灵台县| 灌阳县| 舞钢市| 昌都县| 太仆寺旗| 彭水| 含山县| 黑水县| 三都| 论坛| 辉南县|