First Hops
Hop 0: Getting started
新建Maven項(xiàng)目,修改pom.xml中的依賴
<dependencies>
<!-- The JCR API -->
<dependency>
<groupId>javax.jcr</groupId>
<artifactId>jcr</artifactId>
<version>2.0</version>
</dependency>
<!-- Jackrabbit contentrepository -->
<dependency>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>jackrabbit-core</artifactId>
<version>2.2.4</version>
</dependency>
<!-- Use Log4J for logging-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.11</version>
</dependency>
</dependencies>
你可能 ClassNotFoundException異常信息當(dāng)試圖編譯和履行以下示例時(shí)候
Hop 1: Logging in toJackrabbit
創(chuàng)建1個(gè)jackrabbit內(nèi)容存儲(chǔ)庫并啟動(dòng)1個(gè)登錄會(huì)話來訪問它
import javax.jcr.Repository;
import javax.jcr.Session;
import org.apache.jackrabbit.core.TransientRepository;
/**
* First hop example. Logs in to a content repository and prints a
* status message.
*/
public class FirstHop {
/**
* The main entry point of theexample application.
*
* @param args command linearguments (ignored)
* @throws Exception if an erroroccurs
*/
public static void main(String[]args) throws Exception {
Repository repository = newTransientRepository();
Session session =repository.login();
try {
String user =session.getUserID();
String name =repository.getDescriptor(Repository.REP_NAME_DESC);
System.out.println(
"Logged in as "+ user + " to a " + name + " repository.");
} finally {
session.logout();
}
}
}
Run as->java application
Logged in as anonymous to a Jackrabbit repository.
詳解FirstHop.java文件
import javax.jcr.Repository;
import javax.jcr.Session;
JCR API的接口位于:jcr⑴.0.jar / javax.jcr,該包的所有接口與倉庫的實(shí)現(xiàn)方式無關(guān)
Repository接口代表1個(gè)的倉庫的實(shí)例
Session接口代表訪問倉庫1個(gè)登錄會(huì)話,該會(huì)話訪問倉庫內(nèi)的任何內(nèi)容
注意:如果多線程同時(shí)訪問內(nèi)容,需要開啟多個(gè)會(huì)話(會(huì)話實(shí)例不能保證線程安全)
import org.apache.jackrabbit.core.TransientRepository;
部署jackrabbit推薦使用JNDI
或在1個(gè)使程序代碼不直接依賴jackrabbit的容器環(huán)境,使用其他配置機(jī)制,
如果僅做1個(gè)簡(jiǎn)單獨(dú)立的利用可使用TransientRepository類
public class FirstHop
public static void main(String[] args) throws Exception
作為簡(jiǎn)單利用示例,僅main()方法,使用jvm進(jìn)行異常處理
實(shí)際利用通常是web利用或EJB組件,使用不同的設(shè)置和毛病處理模式
Repository repository = new TransientRepository();
TransientRepository類實(shí)現(xiàn)了Repository接口,可以通過簡(jiǎn)單實(shí)例化TransientRepository對(duì)象,調(diào)用構(gòu)造函數(shù)將在第1次啟動(dòng)會(huì)話時(shí)進(jìn)行進(jìn)行初始配置和倉庫建設(shè)。
因此,除非你想通過庫設(shè)置進(jìn)行直接控制,否則無需手動(dòng)配置。
Session session = repository.login();
Repository.login()開啟1個(gè)使用默許工作空間且沒有用戶憑據(jù)的倉庫會(huì)話
Jackrabbit嘗試使用java認(rèn)證和授權(quán)服務(wù)(JAAS)配置,但對(duì)匿名用戶默許JAAS沒法找到
使Repository接口對(duì)象進(jìn)1步實(shí)例化
try { ... } finally { session.logout(); }
當(dāng)順利履行house,需要釋放所有取得資源且保證JCR會(huì)話不產(chǎn)生異常
finally進(jìn)行釋放資源:Session.logout()關(guān)閉唯1開啟的會(huì)話,transientrepository自動(dòng)關(guān)閉
String user = session.getUserID();
The username or identifier of the user associated witha session is available using the 通過Session.getUserID()方法獲得用戶名或會(huì)話相干的用戶id,若為空則默許返回anonymous
String name = repository.getDescriptor(Repository.REP_NAME_DESC);
每一個(gè)內(nèi)容倉庫實(shí)現(xiàn)都會(huì)產(chǎn)生大量的字符串描寫符來描寫各種實(shí)現(xiàn)屬性(比如:實(shí)現(xiàn)級(jí)別、JCR特性可支持的選項(xiàng))
通過查看Repository接口的標(biāo)準(zhǔn)倉庫描寫列表:
REP_NAME_DESC描寫符的是repository實(shí)現(xiàn)的名稱"Jackrabbit".
Hop 2: Working withcontent
內(nèi)容倉庫的主要功能是存儲(chǔ)和檢索內(nèi)容,在JCR內(nèi)容倉庫的內(nèi)容包括:
結(jié)構(gòu)化或非結(jié)構(gòu)話數(shù)據(jù)模型作為1個(gè)包括實(shí)際數(shù)據(jù)屬性的層次結(jié)構(gòu)的結(jié)點(diǎn)
下面示例利用程序功能:
1.將存用1些內(nèi)容儲(chǔ)初始化為空內(nèi)容倉庫
2.檢索存儲(chǔ)的內(nèi)容并輸出
3.刪除存儲(chǔ)內(nèi)容
import javax.jcr.Repository;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.Node;
import org.apache.jackrabbit.core.TransientRepository;
/**
* Second hop example. Stores, retrieves, and removes example content.
*/
public class SecondHop {
/**
* The main entry point of theexample application.
*
* @param args command linearguments (ignored)
* @throws Exception if an erroroccurs
*/
public static void main(String[]args) throws Exception {
Repository repository = newTransientRepository();
Session session =repository.login(
newSimpleCredentials("username", "password".toCharArray()));
try {
Node root =session.getRootNode();
// Store content
Node hello =root.addNode("hello");
Node world =hello.addNode("world");
world.setProperty("message", "Hello, World!");
session.save();
// Retrieve content
Node node =root.getNode("hello/world");
System.out.println(node.getPath());
System.out.println(node.getProperty("message").getString());
// Remove content
root.getNode("hello").remove();
session.save();
} finally {
session.logout();
}
}
}
運(yùn)行
/hello/world
Hello, World!
以下是與FirstHop的差異部份:
import javax.jcr.SimpleCredentials;
import javax.jcr.Node;
這兩個(gè)類是這個(gè)示例中新添加的類:
SimpleCredentials類是Credentials接口的簡(jiǎn)單實(shí)現(xiàn):
使用Repository.login(Credentials)方法確認(rèn)用戶憑證
Node接口用于管理倉庫中的內(nèi)容結(jié)點(diǎn)。
有1個(gè)相干接口Property用于管理內(nèi)容屬性,但在這個(gè)例子中只間接使用Property接口
new SimpleCredentials("username","password".toCharArray())
在第1個(gè)示例中討論的,在jackrabbit默許配置中Repository.login()方法返回1個(gè)匿名用戶只讀會(huì)話
我們需要通過1些認(rèn)證,來創(chuàng)建寫訪問的會(huì)話來存儲(chǔ)和刪除內(nèi)容
Repository.login(Credentials credentials) method.
默許的jackrabbit登錄機(jī)制接受任何用戶名和密碼作為有效憑據(jù),并返回寫訪問會(huì)話
因此 我們只要構(gòu)造和使用SimpleCredentials示例,1些虛擬的用戶名和密碼:比如"username"和 "password".
SimpleCredentials構(gòu)造方法遵守JAAS:
用戶名作為1個(gè)普通的String類型,但是密碼需要作為1個(gè)char型數(shù)組String.toCharArray()
Node root = session.getRootNode();
每一個(gè)JCR會(huì)話與包括1個(gè)單結(jié)點(diǎn)樹的工作空間有關(guān)。
通過調(diào)用Session.getRootNode()來訪問根結(jié)點(diǎn)
根結(jié)點(diǎn)作為參考,可以輕松地存儲(chǔ)和檢索當(dāng)前工作空間的內(nèi)容
Node hello = root.addNode("hello");
Node world = hello.addNode("world");
新的內(nèi)容結(jié)點(diǎn)可以通過使用Node.addNode(String relPath)添加
該方法用結(jié)點(diǎn)名字(或相對(duì)路徑)添加并創(chuàng)建與當(dāng)前會(huì)話有關(guān)的臨時(shí)存儲(chǔ)相干命名結(jié)點(diǎn),
在臨時(shí)存儲(chǔ)保存成功之前,被添加的結(jié)點(diǎn)僅在當(dāng)前會(huì)話可見并,但在訪問內(nèi)容庫的其他會(huì)話不能訪問
這段代碼創(chuàng)建了兩個(gè)新的結(jié)點(diǎn):"hello"和 "world"
"hello"作為根結(jié)點(diǎn)的子結(jié)點(diǎn)
"world"作為"hello"結(jié)點(diǎn)的子結(jié)點(diǎn)
world.setProperty("message", "Hello, World!");
通過使用"hello"和 "world"結(jié)點(diǎn),創(chuàng)建結(jié)構(gòu),添加1些內(nèi)容
使用Node.setProperty(Stringname, String value)的方法添加1個(gè)屬性:
向world結(jié)點(diǎn)添加message屬性,該屬性值是String類型的"Hello,World!"
像這些被添加的節(jié)點(diǎn),這個(gè)屬性在當(dāng)前會(huì)話的臨時(shí)存儲(chǔ)中首次被創(chuàng)建
如果該屬性的名字已存在,那末這個(gè)方法將會(huì)改變屬性的值
session.save();
雖然我們的示例在單1會(huì)話的臨時(shí)存儲(chǔ)的進(jìn)程中是正常工作的,但我們依然希望可以將到目前為止的修改內(nèi)容進(jìn)行持久化。這樣的話,其他的也能允許訪問我們剛剛創(chuàng)建的示例內(nèi)容。如果你想要的話,你乃至可以把示例城辨別成3塊存儲(chǔ),分別存儲(chǔ),檢索,或刪除示例內(nèi)容。但是條件是必須對(duì)修改內(nèi)容進(jìn)行持久化。Session.save()方法將保持所有在臨時(shí)存儲(chǔ)的所有提交的修改內(nèi)容,修改內(nèi)容將寫入持久的倉庫中存儲(chǔ)并將對(duì)訪問該工作空間的所有會(huì)話可見,如果不調(diào)用該方法,則會(huì)話關(guān)閉時(shí)所有修改內(nèi)容將會(huì)丟失。
Node node = root.getNode("hello/world");
由于我們依然在同1個(gè)會(huì)話,我們通過援用現(xiàn)有的hello和world結(jié)點(diǎn)來訪問存儲(chǔ)內(nèi)容。
但是讓我們假定已開啟了另外一個(gè)會(huì)話,需要檢索之前存儲(chǔ)的內(nèi)容
Node.getNode(StringrelPath)方法返回1個(gè)給定路徑的參考節(jié)點(diǎn)
常見的文件系統(tǒng)路徑語法約定:用/分割節(jié)點(diǎn)名稱,用.表示當(dāng)前節(jié)點(diǎn),用..表示父節(jié)點(diǎn)
"hello/world"表示在根結(jié)點(diǎn)的子節(jié)點(diǎn)hello,hello的子節(jié)點(diǎn)world,world的子節(jié)點(diǎn)是當(dāng)前的節(jié)點(diǎn).
終究的結(jié)果是該方法返回:創(chuàng)建表示與word結(jié)點(diǎn)實(shí)例相同內(nèi)容的結(jié)點(diǎn)
System.out.println(node.getPath());
每一個(gè)內(nèi)容結(jié)點(diǎn)和屬性都是用他在工作空間的絕對(duì)路徑來唯1標(biāo)識(shí)的。
這個(gè)絕對(duì)路徑開始于1個(gè)/和順次包括在當(dāng)前節(jié)點(diǎn)或?qū)傩悦种八懈腹?jié)點(diǎn)
這個(gè)節(jié)點(diǎn)或?qū)傩缘穆窂娇梢酝ㄟ^Item.getPath()取得。這個(gè)Item接口是Node和Property的父接口
,包括所有結(jié)點(diǎn)和屬性同享的全部方法。結(jié)點(diǎn)變量相對(duì)"world"結(jié)點(diǎn)的, 所以輸出 "/hello/world".
System.out.println(node.getProperty("message").getString());
通過Node.getProperty(String relPath)方法,訪問屬性,并返回1個(gè)Property接口的實(shí)例。表示在相對(duì)當(dāng)前節(jié)點(diǎn)給定的相對(duì)路徑的屬性。Message屬性是我們剛創(chuàng)建的1個(gè)屬性
JCR屬性可以包括指定類型的1個(gè)或多個(gè)值。屬性類型可以是字符串,數(shù)字,日期,2進(jìn)制流,參考節(jié)點(diǎn)等等。我們僅需要單1的字符串值,所以調(diào)用Property.getString()方法。結(jié)果是輸出"Hello,World!"
root.getNode("hello").remove();
通過調(diào)用Item.remove()方法可以刪除結(jié)點(diǎn)和屬性.
這個(gè)方法將刪除全部?jī)?nèi)容子樹,我們只需要?jiǎng)h除最上面的"hello"節(jié)點(diǎn),就能夠了刪除我們之前添加的所有內(nèi)容
刪除操作會(huì)首先存儲(chǔ)在會(huì)話本地的臨時(shí)存儲(chǔ),就像添加和修改內(nèi)容。
像之前1樣,這個(gè)臨時(shí)的修改內(nèi)容需要明確保存內(nèi)容將被從持久化存儲(chǔ)中刪除。
Hop 3: Importing content ―――――――― TODO: Update tomatch the style of previous hops.
添加內(nèi)容更有效果,可使用JCR導(dǎo)入配置,比如Seession.importxml.線面的xml文檔由Elliotte Rusty Harold提供1個(gè)又去的實(shí)例:演示1個(gè)庫的命名空間的作用
test.xml
<xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:mathml="http://www.w3.org/1998/Math/MathML">
<xhtml:head><xhtml:title>ThreeNamespaces</xhtml:title></xhtml:head>
<xhtml:body>
<xhtml:h1 align="center">An Ellipse and aRectangle</xhtml:h1>
<svg:svg xmlns:svg="http://www.w3.org/2000/svg"
width="12cm" height="10cm">
<svg:ellipse rx="110" ry="130" />
<svg:rect x="4cm" y="1cm" width="3cm"height="6cm" />
</svg:svg>
<xhtml:p>The equation for ellipses</xhtml:p>
<mathml:math>
<mathml:apply>
<mathml:eq/>
<mathml:cn> 1 </mathml:cn>
<mathml:apply>
<mathml:plus/>
<mathml:apply>
<mathml:divide/>
<mathml:apply>
<mathml:power/>
<mathml:ci> x </mathml:ci>
<mathml:cn> 2 </mathml:cn>
</mathml:apply>
<mathml:apply>
<mathml:power/>
<mathml:ci> a </mathml:ci>
<mathml:cn> 2 </mathml:cn>
</mathml:apply>
</mathml:apply>
<mathml:apply>
<mathml:divide/>
<mathml:apply>
<mathml:power/>
<mathml:ci> y </mathml:ci>
<mathml:cn> 2 </mathml:cn>
</mathml:apply>
<mathml:apply>
<mathml:power/>
<mathml:ci> b </mathml:ci>
<mathml:cn> 2 </mathml:cn>
</mathml:apply>
</mathml:apply>
</mathml:apply>
</mathml:apply>
</mathml:math>
<xhtml:hr/>
<xhtml:p>Last Modified January 10, 2002</xhtml:p>
</xhtml:body>
</xhtml:html>
下面的第3個(gè)實(shí)例程序?qū)⒄宫F(xiàn)導(dǎo)入名為test.xml的xml文件。從當(dāng)前目錄到1個(gè)名為importxml的新的內(nèi)容倉庫節(jié)點(diǎn)。1旦xml內(nèi)容導(dǎo)入,程序會(huì)調(diào)用dump()方法遞歸的輸出全部工作區(qū)的內(nèi)容
import javax.jcr.*;
import org.apache.jackrabbit.core.TransientRepository;
import java.io.FileInputStream;
/**
* Third Jackrabbit example application. Imports an example XML file
* and outputs the contents of the entire workspace.
*/
public class ThirdHop {
/**
* The main entry point of theexample application.
*
* @param args command linearguments (ignored)
* @throws Exception if an erroroccurs
*/
public static void main(String[]args) throws Exception {
Repository repository = newTransientRepository();
Session session =repository.login(
newSimpleCredentials("username", "password".toCharArray()));
try {
Node root =session.getRootNode();
// Import the XML file unlessalready imported
if(!root.hasNode("importxml")) {
System.out.print("Importing xml... ");
// Create anunstructured node under which to import the XML
Node node =root.addNode("importxml", "nt:unstructured");
// Import thefile "test.xml" under the created node
FileInputStreamxml = new FileInputStream("test.xml");
session.importXML(
node.getPath(),xml, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
xml.close();
session.save();
System.out.println("done.");
}
//output therepository content
dump(root);
} finally {
session.logout();
}
}
/** Recursively outputs thecontents of the given node. */
private static void dump(Nodenode) throws RepositoryException {
// First output the node path
System.out.println(node.getPath());
// Skip the virtual (andlarge!) jcr:system subtree
if(node.getName().equals("jcr:system")) {
return;
}
// Then output the properties
PropertyIterator properties =node.getProperties();
while (properties.hasNext()){
Property property =properties.nextProperty();
if(property.getDefinition().isMultiple()) {
// A multi-valuedproperty, print all values
Value[] values =property.getValues();
for (int i = 0; i< values.length; i++) {
System.out.println(
property.getPath() + " = " + values[i] .getString());
}
} else {
// A single-valuedproperty
System.out.println(
property.getPath() +" = " + property.getString());
}
}
// Finally output all thechild nodes recursively
NodeIterator nodes =node.getNodes();
while (nodes.hasNext()) {
dump(nodes.nextNode());
}
}
}
運(yùn)行第3個(gè)示例,結(jié)果以下
Importing XML... done.
/
/jcr:primaryType=rep:root
/jcr:system
/importxml
/importxml/jcr:primaryType=nt:unstructured
/importxml/xhtml:html
/importxml/xhtml:html/jcr:primaryType=nt:unstructured
/importxml/xhtml:html/xhtml:head
/importxml/xhtml:html/xhtml:head/jcr:primaryType=nt:unstructured
/importxml/xhtml:html/xhtml:head/xhtml:title
/importxml/xhtml:html/xhtml:head/xhtml:title/jcr:primaryType=nt:unstructured
/importxml/xhtml:html/xhtml:head/xhtml:title/jcr:xmltext
/importxml/xhtml:html/xhtml:head/xhtml:title/jcr:xmltext/jcr:primaryType=nt:unstructured
/importxml/xhtml:html/xhtml:head/xhtml:title/jcr:xmltext/jcr:xmlcharacters=ThreeNamespaces
/importxml/xhtml:html/xhtml:body
/importxml/xhtml:html/xhtml:body/jcr:primaryType=nt:unstructured
/importxml/xhtml:html/xhtml:body/xhtml:h1
/importxml/xhtml:html/xhtml:body/xhtml:h1/jcr:primaryType=nt:unstructured
/importxml/xhtml:html/xhtml:body/xhtml:h1/align=center
/importxml/xhtml:html/xhtml:body/xhtml:h1/jcr:xmltext
/importxml/xhtml:html/xhtml:body/xhtml:h1/jcr:xmltext/jcr:primaryType=nt:unstructured
/importxml/xhtml:html/xhtml:body/xhtml:h1/jcr:xmltext/jcr:xmlcharacters=AnEllipse and a Rectangle
/importxml/xhtml:html/xhtml:body/svg:svg
/importxml/xhtml:html/xhtml:body/svg:svg/jcr:primaryType=nt:unstructured
/importxml/xhtml:html/xhtml:body/svg:svg/width=12cm
/importxml/xhtml:html/xhtml:body/svg:svg/height=10cm
這個(gè)示例和第2個(gè)示例相比有很多相似的地方:我們創(chuàng)建登錄1個(gè)寫訪問的新會(huì)話。接著插入數(shù)據(jù)到倉庫中。這次通過導(dǎo)入1個(gè)xml文件并且終究我們輸出全部庫的內(nèi)容
現(xiàn)在你應(yīng)當(dāng)熟習(xí)登錄1個(gè)倉庫(Repository.login)
在倉庫根結(jié)點(diǎn)下(Session.getRootNode),創(chuàng)建1個(gè)新的節(jié)點(diǎn)(Node.addNode)
并在關(guān)閉保存會(huì)話時(shí),將我們修改內(nèi)容進(jìn)行持久化,(Session.save)
讓我們看看這個(gè)例子中使用的新方法,如何導(dǎo)入xml內(nèi)容
session.importXML(node.getPath(), xml, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
將1個(gè)xml文檔解析,并添加解析的結(jié)果項(xiàng)目字段到給定路徑的1個(gè)子節(jié)點(diǎn)
ImportUUIDBehavior標(biāo)志控制如何處理傳入的節(jié)點(diǎn)標(biāo)示符
有以下4種選項(xiàng)
? ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW:
輸入節(jié)點(diǎn)將和Node.addNode方法1樣被添加。他們也能夠指定新創(chuàng)建的標(biāo)示符添加或保存修改 在任何情況下標(biāo)示符重復(fù)是不會(huì)產(chǎn)生的
? ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING:
如果輸入節(jié)點(diǎn)有1個(gè)相同的標(biāo)示符結(jié)點(diǎn)在工作空間已存在,那末在輸入節(jié)點(diǎn)添加上前,不管在工作空間的甚么位置已存在的節(jié)點(diǎn)和子節(jié)點(diǎn)將被刪除, 請(qǐng)注意,該節(jié)點(diǎn)將從本地的工作空間消失,遠(yuǎn)程獲得的輸入結(jié)點(diǎn)及子節(jié)點(diǎn)將會(huì)被寫入。刪除和添加都將分別被保存
? ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING:
如果輸入節(jié)點(diǎn)有1個(gè)相同的標(biāo)示符結(jié)點(diǎn)在工作空間已存在,那末已存在的節(jié)點(diǎn)將會(huì)在相同的位置替換為輸入節(jié)點(diǎn).請(qǐng)注意,這可能致使子節(jié)點(diǎn)作為輸入和向周圍傳播到不同位置的工作空間。在最極真?zhèn)€例子,可能會(huì)致使沒有節(jié)點(diǎn)會(huì)被添加在該父節(jié)點(diǎn)的絕對(duì)路徑的子節(jié)點(diǎn).如果如數(shù)的xml的最頂真?zhèn)€元素有相同的標(biāo)示符在現(xiàn)有的工作空間。這些改變將被分別保存.
? ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW:
如果輸入節(jié)點(diǎn)有1個(gè)相同的標(biāo)示符結(jié)點(diǎn)在工作空間已存,那末將會(huì)拋出1ItemExistsException異常
另外一個(gè)有趣的方法是
voiddump(Node node)
調(diào)用該方法將解決如何遞歸遍歷示例的所有庫,并且檢查每一個(gè)節(jié)點(diǎn)屬性。
要注意多值的屬性,我們使用它們需要調(diào)用
property.getDefinition().isMultiple()
如果返回true那末就是1個(gè)多值屬性
Value[] values = property.getValues();
否則我們調(diào)用api 獲得需要的值
property.getString()
這相當(dāng)于
property.getValue().getString()
對(duì)事務(wù)相干的代碼示例是JcrUtils.