apache jackrabbit First hop

来源:互联网 发布:软件测试的功能 编辑:程序博客网 时间:2024/06/04 18:40

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

创建一个jackrabbit内容存储库并启动一个登录会话来访问它

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-1.0.jar / javax.jcr,该包的所有接口与仓库的实现方式无关

Repository接口代表一个的仓库的实例

Session接口代表访问仓库一个登录会话,该会话访问仓库内的任何内容

注意:如果多线程同时访问内容,需要开启多个会话(会话实例不能保证线程安全)

import org.apache.jackrabbit.core.TransientRepository;

部署jackrabbit推荐使用JNDI

或者在一个使程序代码不直接依赖jackrabbit的容器环境,使用其他配置机制,

如果仅做一个简单独立的应用可以使用TransientRepository

public class FirstHop

public static void main(String[] args) throws Exception

作为简单应用示例,仅main()方法,使用jvm进行异常处理

实际应用通常是web应用或者EJB组件,使用不同的设置和错误处理模式

Repository repository = new TransientRepository();

TransientRepository类实现了Repository接口,可以通过简单实例化TransientRepository对象,调用构造函数将在第一次启动会话时进行进行初始配置和仓库建设。

因此,除非你想通过库设置进行直接控制,否则无需手动配置。

Session session = repository.login();

Repository.login()开启一个使用默认工作空间且没有用户凭据的仓库会话

Jackrabbit尝试使用java认证和授权服务(JAAS)配置,但对于匿名用户默认JAAS无法找到

使Repository接口对象进一步实例化

 

try { ... } finally { session.logout(); }

当顺利执行house,需要释放所有获得资源且保证JCR会话不发生异常

finally进行释放资源:Session.logout()关闭唯一开启的会话,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.将存用一些内容储初始化为空内容仓库

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接口用于管理仓库中的内容结点。

有一个相关接口Property用于管理内容属性,但在这个例子中只间接使用Property接口

new SimpleCredentials("username","password".toCharArray())

在第一个示例中讨论的,在jackrabbit默认配置中Repository.login()方法返回一个匿名用户只读会话

我们需要通过一些认证,来创建写访问的会话来存储和删除内容

Repository.login(Credentials credentials) method.

默认的jackrabbit登录机制接受任何用户名和密码作为有效凭据,并返回写访问会话

因此 我们只要构造和使用SimpleCredentials示例,一些虚拟的用户名和密码:比如"username""password".

SimpleCredentials构造方法遵循JAAS

用户名作为一个普通的String类型,但是密码需要作为一个char型数组String.toCharArray()

Node root = session.getRootNode();

每个JCR会话与包含一个单结点树的工作空间有关。

通过调用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"结点,创建结构,添加一些内容

使用Node.setProperty(Stringname, String value)的方法添加一个属性:

向world结点添加message属性,该属性值是String类型的"Hello,World!"

像这些被添加的节点,这个属性在当前会话的临时存储中首次被创建

如果该属性的名字已经存在,那么这个方法将会改变属性的值

session.save();

尽管我们的示例在单一会话的临时存储的过程中是正常工作的,但我们仍然希望可以将到目前为止的修改内容进行持久化。这样的话,其他的也能允许访问我们刚刚创建的示例内容。如果你想要的话,你甚至可以把示例城区分成三块存储,分别存储,检索,或删除示例内容。但是前提是必须对修改内容进行持久化。Session.save()方法将保持所有在临时存储的所有提交的修改内容,修改内容将写入持久的仓库中存储并将对访问该工作空间的所有会话可见,如果不调用该方法,则会话关闭时所有修改内容将会丢失。

Node node = root.getNode("hello/world");

由于我们仍然在同一个会话,我们通过引用现有的hello和world结点来访问存储内容。

但是让我们假设已经开启了另一个会话,需要检索以前存储的内容

Node.getNode(StringrelPath)方法返回一个给定路径的参考节点

常见的文件系统路径语法约定:用/分割节点名称,用.表示当前节点,用..表示父节点

"hello/world"表示在根结点的子节点hello,hello的子节点world,world的子节点是当前的节点.

最终的结果是该方法返回:创建表示与word结点实例相同内容的结点

System.out.println(node.getPath());

每个内容结点和属性都是用他在工作空间的绝对路径来唯一标识的。

这个绝对路径开始于一个/和依次包含在当前节点或属性名字之前所有父节点

这个节点或属性的路径可以通过Item.getPath()获得。这个Item接口是Node和Property的父接口

,包含所有结点和属性共享的全部方法。结点变量相对于"world"结点的,所以输出 "/hello/world".

System.out.println(node.getProperty("message").getString());

通过Node.getProperty(String relPath)方法,访问属性,并返回一个Property接口的实例。表示在相对于当前节点给定的相对路径的属性。Message属性是我们刚创建的一个属性

JCR属性可以包含指定类型的一个或多个值。属性类型可以是字符串,数字,日期,二进制流,参考节点等等。我们仅需要单一的字符串值,所以调用Property.getString()方法。结果是输出"Hello,World!"

root.getNode("hello").remove();

通过调用Item.remove()方法可以删除结点和属性.

这个方法将删除整个内容子树,我们只需要删除最上面的"hello"节点,就可以了删除我们之前添加的所有内容

删除操作会首先存储在会话本地的临时存储,就像添加和修改内容。

像以前一样,这个临时的修改内容需要明确保存内容将被从持久化存储中删除。

Hop 3: Importing content ———————— TODO: Update tomatch the style of previous hops.

添加内容更有效果,可以使用JCR导入配置,比如Seession.importxml.线面的xml文档由Elliotte Rusty Harold提供一个又去的实例:演示一个库的命名空间的作用

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>

下面的第三个实例程序将展示导入名为test.xml的xml文件。从当前目录到一个名为importxml的新的内容仓库节点。一旦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());

       }

    }

}

运行第三个示例,结果如下

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

这个示例和第二个示例相比有很多相似之处:我们创建登录一个写访问的新会话。接着插入数据到仓库中。这次通过导入一个xml文件并且最终我们输出整个库的内容

现在你应该熟悉登录一个仓库(Repository.login

在仓库根结点下(Session.getRootNode),创建一个新的节点(Node.addNode)

并在关闭保存会话时,将我们修改内容进行持久化,(Session.save)

让我们看看这个例子中使用的新方法,如何导入xml内容

session.importXML(node.getPath(), xml, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);

将一个xml文档解析,并添加解析的结果项目字段到给定路径的一个子节点

ImportUUIDBehavior标志控制如何处理传入的节点标示符

有以下四种选项

·        ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW:
输入节点将和Node.addNode方法一样被添加。他们也可以指定新创建的标示符添加或保存修改 在任何情况下标示符重复是不会发生的

·        ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING:
如果输入节点有一个相同的标示符结点在工作空间已经存在,那么在输入节点添加之前,无论在工作空间的什么位置已经存在的节点和子节点将被删除, 请注意,该节点将从本地的工作空间消失,远程获取的输入结点及子节点将会被写入。删除和添加都将分别被保存

·        ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING:
如果输入节点有一个相同的标示符结点在工作空间已经存在,那么已经存在的节点将会在相同的位置替换为输入节点.请注意,这可能导致子节点作为输入和向周围传播到不同位置的工作空间。在最极端的例子,可能会导致没有节点会被添加在该父节点的绝对路径的子节点.如果如数的xml的最顶端的元素有相同的标示符在现有的工作空间。这些改变将被分别保存.

·        ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW:
如果输入节点有一个相同的标示符结点在工作空间已经存,那么将会抛出一ItemExistsException异常

另一个有趣的方法是

voiddump(Node node)                                                                                         

调用该方法将解决如何递归遍历示例的所有库,并且检查每个节点属性。

要注意多值的属性,我们使用它们需要调用

property.getDefinition().isMultiple()

如果返回true那么就是一个多值属性

Value[] values = property.getValues();

否则我们调用api 获取需要的值

property.getString()

这相当于

property.getValue().getString()

对于事务相关的代码示例是JcrUtils.

0 0