First Hops
Hop 0: Getting started
新建Maven项目,修改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异常信息当试图编译和履行以下示例时候
Hop 1: Logging in toJackrabbit
创建1个jackrabbit内容存储库并启动1个登录会话来访问它
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,该包的所有接口与仓库的实现方式无关
Repository接口代表1个的仓库的实例
Session接口代表访问仓库1个登录会话,该会话访问仓库内的任何内容
注意:如果多线程同时访问内容,需要开启多个会话(会话实例不能保证线程安全)
import org.apache.jackrabbit.core.TransientRepository;
部署jackrabbit推荐使用JNDI
或在1个使程序代码不直接依赖jackrabbit的容器环境,使用其他配置机制,
如果仅做1个简单独立的利用可使用TransientRepository类
public class FirstHop
public static void main(String[] args) throws Exception
作为简单利用示例,仅main()方法,使用jvm进行异常处理
实际利用通常是web利用或EJB组件,使用不同的设置和毛病处理模式
Repository repository = new TransientRepository();
TransientRepository类实现了Repository接口,可以通过简单实例化TransientRepository对象,调用构造函数将在第1次启动会话时进行进行初始配置和仓库建设。
因此,除非你想通过库设置进行直接控制,否则无需手动配置。
Session session = repository.login();
Repository.login()开启1个使用默许工作空间且没有用户凭据的仓库会话
Jackrabbit尝试使用java认证和授权服务(JAAS)配置,但对匿名用户默许JAAS没法找到
使Repository接口对象进1步实例化
try { ... } finally { session.logout(); }
当顺利履行house,需要释放所有取得资源且保证JCR会话不产生异常
finally进行释放资源:Session.logout()关闭唯1开启的会话,transientrepository自动关闭
String user = session.getUserID();
The username or identifier of the user associated witha session is available using the 通过Session.getUserID()方法获得用户名或会话相干的用户id,若为空则默许返回anonymous
String name = repository.getDescriptor(Repository.REP_NAME_DESC);
每一个内容仓库实现都会产生大量的字符串描写符来描写各种实现属性(比如:实现级别、JCR特性可支持的选项)
通过查看Repository接口的标准仓库描写列表:
REP_NAME_DESC描写符的是repository实现的名称"Jackrabbit".
Hop 2: Working withcontent
内容仓库的主要功能是存储和检索内容,在JCR内容仓库的内容包括:
结构化或非结构话数据模型作为1个包括实际数据属性的层次结构的结点
下面示例利用程序功能:
1.将存用1些内容储初始化为空内容仓库
2.检索存储的内容并输出
3.删除存储内容
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();
}
}
}
运行
/hello/world
Hello, World!
以下是与FirstHop的差异部份:
import javax.jcr.SimpleCredentials;
import javax.jcr.Node;
这两个类是这个示例中新添加的类:
SimpleCredentials类是Credentials接口的简单实现:
使用Repository.login(Credentials)方法确认用户凭证
Node接口用于管理仓库中的内容结点。
有1个相干接口Property用于管理内容属性,但在这个例子中只间接使用Property接口
new SimpleCredentials("username","password".toCharArray())
在第1个示例中讨论的,在jackrabbit默许配置中Repository.login()方法返回1个匿名用户只读会话
我们需要通过1些认证,来创建写访问的会话来存储和删除内容
Repository.login(Credentials credentials) method.
默许的jackrabbit登录机制接受任何用户名和密码作为有效凭据,并返回写访问会话
因此 我们只要构造和使用SimpleCredentials示例,1些虚拟的用户名和密码:比如"username"和 "password".
SimpleCredentials构造方法遵守JAAS:
用户名作为1个普通的String类型,但是密码需要作为1个char型数组String.toCharArray()
Node root = session.getRootNode();
每一个JCR会话与包括1个单结点树的工作空间有关。
通过调用Session.getRootNode()来访问根结点
根结点作为参考,可以轻松地存储和检索当前工作空间的内容
Node hello = root.addNode("hello");
Node world = hello.addNode("world");
新的内容结点可以通过使用Node.addNode(String relPath)添加
该方法用结点名字(或相对路径)添加并创建与当前会话有关的临时存储相干命名结点,
在临时存储保存成功之前,被添加的结点仅在当前会话可见并,但在访问内容库的其他会话不能访问
这段代码创建了两个新的结点:"hello"和 "world"
"hello"作为根结点的子结点
"world"作为"hello"结点的子结点
world.setProperty("message", "Hello, World!");
通过使用"hello"和 "world"结点,创建结构,添加1些内容
使用Node.setProperty(Stringname, String value)的方法添加1个属性:
向world结点添加message属性,该属性值是String类型的"Hello,World!"
像这些被添加的节点,这个属性在当前会话的临时存储中首次被创建
如果该属性的名字已存在,那末这个方法将会改变属性的值
session.save();
虽然我们的示例在单1会话的临时存储的进程中是正常工作的,但我们依然希望可以将到目前为止的修改内容进行持久化。这样的话,其他的也能允许访问我们刚刚创建的示例内容。如果你想要的话,你乃至可以把示例城辨别成3块存储,分别存储,检索,或删除示例内容。但是条件是必须对修改内容进行持久化。Session.save()方法将保持所有在临时存储的所有提交的修改内容,修改内容将写入持久的仓库中存储并将对访问该工作空间的所有会话可见,如果不调用该方法,则会话关闭时所有修改内容将会丢失。
Node node = root.getNode("hello/world");
由于我们依然在同1个会话,我们通过援用现有的hello和world结点来访问存储内容。
但是让我们假定已开启了另外一个会话,需要检索之前存储的内容
Node.getNode(StringrelPath)方法返回1个给定路径的参考节点
常见的文件系统路径语法约定:用/分割节点名称,用.表示当前节点,用..表示父节点
"hello/world"表示在根结点的子节点hello,hello的子节点world,world的子节点是当前的节点.
终究的结果是该方法返回:创建表示与word结点实例相同内容的结点
System.out.println(node.getPath());
每一个内容结点和属性都是用他在工作空间的绝对路径来唯1标识的。
这个绝对路径开始于1个/和顺次包括在当前节点或属性名字之前所有父节点
这个节点或属性的路径可以通过Item.getPath()取得。这个Item接口是Node和Property的父接口
,包括所有结点和属性同享的全部方法。结点变量相对"world"结点的, 所以输出 "/hello/world".
System.out.println(node.getProperty("message").getString());
通过Node.getProperty(String relPath)方法,访问属性,并返回1个Property接口的实例。表示在相对当前节点给定的相对路径的属性。Message属性是我们刚创建的1个属性
JCR属性可以包括指定类型的1个或多个值。属性类型可以是字符串,数字,日期,2进制流,参考节点等等。我们仅需要单1的字符串值,所以调用Property.getString()方法。结果是输出"Hello,World!"
root.getNode("hello").remove();
通过调用Item.remove()方法可以删除结点和属性.
这个方法将删除全部内容子树,我们只需要删除最上面的"hello"节点,就能够了删除我们之前添加的所有内容
删除操作会首先存储在会话本地的临时存储,就像添加和修改内容。
像之前1样,这个临时的修改内容需要明确保存内容将被从持久化存储中删除。
Hop 3: Importing content ―――――――― TODO: Update tomatch the style of previous hops.
添加内容更有效果,可使用JCR导入配置,比如Seession.importxml.线面的xml文档由Elliotte Rusty Harold提供1个又去的实例:演示1个库的命名空间的作用
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个实例程序将展现导入名为test.xml的xml文件。从当前目录到1个名为importxml的新的内容仓库节点。1旦xml内容导入,程序会调用dump()方法递归的输出全部工作区的内容
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());
}
}
}
运行第3个示例,结果以下
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
这个示例和第2个示例相比有很多相似的地方:我们创建登录1个写访问的新会话。接着插入数据到仓库中。这次通过导入1个xml文件并且终究我们输出全部库的内容
现在你应当熟习登录1个仓库(Repository.login)
在仓库根结点下(Session.getRootNode),创建1个新的节点(Node.addNode)
并在关闭保存会话时,将我们修改内容进行持久化,(Session.save)
让我们看看这个例子中使用的新方法,如何导入xml内容
session.importXML(node.getPath(), xml, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
将1个xml文档解析,并添加解析的结果项目字段到给定路径的1个子节点
ImportUUIDBehavior标志控制如何处理传入的节点标示符
有以下4种选项
・ ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW:
输入节点将和Node.addNode方法1样被添加。他们也能够指定新创建的标示符添加或保存修改 在任何情况下标示符重复是不会产生的
・ ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING:
如果输入节点有1个相同的标示符结点在工作空间已存在,那末在输入节点添加上前,不管在工作空间的甚么位置已存在的节点和子节点将被删除, 请注意,该节点将从本地的工作空间消失,远程获得的输入结点及子节点将会被写入。删除和添加都将分别被保存
・ ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING:
如果输入节点有1个相同的标示符结点在工作空间已存在,那末已存在的节点将会在相同的位置替换为输入节点.请注意,这可能致使子节点作为输入和向周围传播到不同位置的工作空间。在最极真个例子,可能会致使没有节点会被添加在该父节点的绝对路径的子节点.如果如数的xml的最顶真个元素有相同的标示符在现有的工作空间。这些改变将被分别保存.
・ ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW:
如果输入节点有1个相同的标示符结点在工作空间已存,那末将会抛出1ItemExistsException异常
另外一个有趣的方法是
voiddump(Node node)
调用该方法将解决如何递归遍历示例的所有库,并且检查每一个节点属性。
要注意多值的属性,我们使用它们需要调用
property.getDefinition().isMultiple()
如果返回true那末就是1个多值属性
Value[] values = property.getValues();
否则我们调用api 获得需要的值
property.getString()
这相当于
property.getValue().getString()
对事务相干的代码示例是JcrUtils.