Jackrabbit 学习笔记 1 入门

最近部门计划把内部多套 CMS 系统整合,如 Portal 的 CMS、微信的 CMS 等系统。怎么搞暂时还木有想法,只能先研究研究 CMS 有啥标准规范了。

因为是工作任务,估计是不会烂尾的,给自己加个油吧!

A. Jackrabbit 简介

http://jackrabbit.apache.org/getting-started-with-apache-jackrabbit.html

Jackrabbit is a complete implementation of the JCR API, so the primary API for Jackrabbit application developers is to be found in the JCR section of the Documentation.
Jackrabbit 完全实现了 JCR API,所以说想知道 Jackrabbit 能做什么就得先了解 JCR 文档。

好吧,JCR 文档在这儿
https://jcp.org/aboutJava/communityprocess/final/jsr170/index.html

唉,jsr文档啊!哪能一上来就啃 jsr 文档啊,找死啊!还是看看前人总结,写几个简单例子吧。

B. JCR 是什么

以下来自百度百科:
Java Content Repository API(JSR-170)试图建立一套标准的API去访问内容仓库
内容仓库可以理解为一个用来存储文本和二进制数据(图片,word文档,PDF等等)的数据存储应用程序。
你不用关心你真正的数据到底存储在什么地方,是关系数据库?是文件系统?还是XML?
大多数的内容仓库还提供了更加高级的功能,例如 访问控制,查找,版本控制,锁定内容 等等。
通过JSR-170,你开发代码只需要引用 javax.jcr.* 这些类和接口。它适用于任何兼容JSR-170规范的内容仓库。

JSR-170 API对不同的人员提供了不同的好处。
●对于开发者无需了解厂家的仓库特定的API,只要兼容JSR-170就可以通过JSR-170访问其仓库。
●对于使用CMS的公司则无需花费资金用于在不同种类CMS的内容仓库之间进行转换。
●对于CMS厂家,无需自己开发内容仓库,而专注于开发CMS应用。

JCR定义的内容仓库模型是一个树状结构,树上的元素(Item)分为两类:节点(node)属性(property)。整棵树具有单根结构。从根开始,内容元素的定位采用UNIX文件系统风格,例如“/A/B/ccc”(根下面A节点的B子节点的ccc属性),相对定位符(“.”和“..”)也是可用的。每个属性有且仅有一个父节点,不能有子元素。每个节点可以有一个或多个父节点(根节点无父节点),也可以有任意多个子节点或属性。注意,节点可以有多个父节点,也就是说可以从不同的路径导航获得同一内容,这是网站常见的一个需求(来自不同栏目的两个链接指向同一篇文章)。

除了 Node 和 Property 之外,API中还有几个重要的对象。Repository 代表整个内容仓库Credentials 代表一个用户身份Ticket 代表进入该内容仓库的门票


她(度娘)说的好清楚,看完之后我竟觉得我已是 JCR 专家了……

看起来,“正规”的 CMS 系统会使用“正规”的内容仓库,即兼容 JSR-170 的内容仓库;同时,“正规”的 CMS 系统应该是内容与展现分离的,也就是说只要通过 JCR API 就能直接获取 CMS 系统的所有“内容”了。看起来很美,不知道具体情况如何。

C. Jackrabbit Standalone Server

http://jackrabbit.apache.org/standalone-server.html

在官网下载一个独立服务器,文件名类似 jackrabbit-standalone-2.10.0.jar,双击 jar 包就启动了该独立服务器。
访问 http://localhost:8080/ 就能使用 Jackrabbit 了。

比双击更好的启动方式是:
java -Xmx256m -jar jackrabbit-standalone-2.10.0.jar -p 8082

启动参数表可用 –help 查看:
java -jar jackrabbit-standalone-2.10.0.jar –help

独立服务器是用来体验 Jackrabbit 功能的。以下代码并不需要独立服务器,所以可以先不急着下载。

D. JCR API 初体验

http://jackrabbit.apache.org/first-hops.html

项目中添加依赖:

        
        <dependency>
            <groupId>javax.jcrgroupId>
            <artifactId>jcrartifactId>
            <version>2.0version>
        dependency>

        
        <dependency>
            <groupId>org.apache.jackrabbitgroupId>
            <artifactId>jackrabbit-coreartifactId>
            <version>2.10.0version>
        dependency>

按理说直接把 jackrabbit-standalone-2.10.0.jar 放入项目 classpath 中也是可以的,未测试。

Sample 1:登录到 Jackrabbit

import javax.jcr.GuestCredentials;
import javax.jcr.Repository;
import javax.jcr.Session;
import org.apache.jackrabbit.commons.JcrUtils;

/**
 * First hop example. Logs in to a content repository and prints a status message.
 */
public class FirstHop {
    public static void main(String[] args) throws Exception {
        Repository repository = JcrUtils.getRepository();
        Session session = repository.login(new GuestCredentials());
        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();
        }
    }
}

Repository 接口代表一个指定的内容仓库。Session 接口代表访问内容仓库的一个会话。Session 实例不是线程安全的!

Jackrabbit 最好部署在有配置机制的容器环境中,例如 JNDI,这样可以保证应用程序代码与 Jackrabbit 无直接依赖。例子代码简单的使用 jackrabbit-commons 组件提供的工具类来演示功能。显然,例子代码与 Jackrabbit 已绑定在一起。

Repository repository = JcrUtils.getRepository();

getRepository() 方法内部使用 JCR RepositoryFactory 机制提供一个 Repository 的实例。Jackrabbit 返回的是 TransientRepository 实例。TransientRepository 会在首个 session 开始工作时自动初始化配置、构造内容仓库,使用起来很方便。同时,如果所有的 session 都已关闭,TransientRepository 会自动关闭内容仓库,所以无需显式的关闭 TransientRepository。

Jackrabbit 仓库目录下包含一个锁文件,用来防止多个进程同时访问这个仓库目录。如果 Repository 没有正常关闭程序就已退出,那这个锁文件将不会被删除。锁文件可以手动删除,但往往意味着内容仓库已经受损。

Session session = repository.login(new GuestCredentials());

Repository.login(Credentials) 方法开启一个会话。该会话使用默认工作区(default workspace)和指定用户身份。GuestCredentials 即匿名用户。

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

Session.logout() 方法关闭会话。在本例中,关闭了唯一的会话后,TransientRepository 也自动被关闭。

String name = repository.getDescriptor(Repository.REP_NAME_DESC);

获取内容仓库描述符(descriptor)的值。不同的内容仓库实现可能有不同的值,例如 Jackrabbit 的 REP_NAME_DESC 描述符的值为 “Jackrabbit”。


Sample 1 自由玩耍部分:

源程序 sysout 前加个

new Scanner(System.in).nextLine();

运行 FirstHop 程序,再新运行一次 FirstHop,会抛出以下异常:

2015/03/30-13:32:25.831 WARN [main] org.apache.jackrabbit.core.util.RepositoryLock:134 >> Existing lock file D:\ge\workspace_dev\prototype\jackrabbit.lock detected. Repository was not shut down properly.
Exception in thread “main” javax.jcr.RepositoryException: The repository home D:\ge\workspace_dev\prototype\jackrabbit appears to be in use since the file named .lock is locked by another process.

首先, FirstHop 运行时会在项目(prototype 项目)目录下创建 jackrabbit 目录,而这个目录就是一个内容仓库。

FirstHop 运行时会创建一个 .lock 文件。当该锁文件存在时,其它进程无法访问该仓库。FirstHop 执行完毕时会删除这个文件。

Sample 2:处理内容

    public static void main(String[] args) throws Exception {
        Repository repository = JcrUtils.getRepository();
        // 用指定的用户身份登录系统
        // Jackrabbit 默认账户 admin/admin
        Session session = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
        try {
            // 每个会话关联一个工作区(workspace),工作区包含一个节点树
            // 获取根节点。(不同用户根节点可能不同?)
            Node root = session.getRootNode();

            // 增加节点和属性
            Node hello = root.addNode("hello");
            Node world = hello.addNode("world");
            world.setProperty("message", "Hello, World!");

            // 当前会话对节点/属性的修改对别的会话不可见,直到当前会话调用 save 方法
            session.save();

            // 获取内容
            Node node = root.getNode("hello/world");
            System.out.println(node.getPath());
            System.out.println(node.getProperty("message").getString());

            // 移除内容。移除整个子树
            root.getNode("hello").remove();
            session.save();
        } finally {
            session.logout();
        }
    }

每个会话关联一个工作区(workspace),工作区包含一个节点树。可以用绝对路径表示一个节点,从根节点开始,如 /hello/world

属性可以包含多个值。属性类型有 strings, numbers, dates, binary streams, node references 等.

Sample 3:导入内容

    public static void main(String[] args) throws Exception {
        Repository repository = JcrUtils.getRepository();
        Session session = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
        try {
            Node root = session.getRootNode();

            // Import the XML file unless already imported
            if (!root.hasNode("importxml")) {
                System.out.print("Importing xml... ");

                // 创建一个无结构节点
                Node node = root.addNode("importxml", "nt:unstructured");

                // 在创建的节点中导入文件
                FileInputStream xml = new FileInputStream(ThirdHop.class.getResource("test.xml").getFile());
                session.importXML(node.getPath(), xml, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
                xml.close();
                session.save();
                System.out.println("done.");
            }

            // 输出内容
            dump(root);
        } finally {
            session.logout();
        }
    }

    /** 递归输出指定节点的内容  */
    private static void dump(Node node) throws RepositoryException {
        // 首先输出节点路径
        System.out.println(node.getPath());
        // 跳过虚节点  jcr:system
        if (node.getName().equals("jcr:system")) {
            return;
        }

        // 接着输出属性
        PropertyIterator properties = node.getProperties();
        while (properties.hasNext()) {
            Property property = properties.nextProperty();
            if (property.getDefinition().isMultiple()) {
                // 多值属性,输出所有属性值
                Value[] values = property.getValues();
                for (int i = 0; i < values.length; i++) {
                    System.out.println(property.getPath() + " = " + values[i].getString());
                }
            } else {
                // 单值属性
                System.out.println(property.getPath() + " = " + property.getString());
            }
        }

        // 最后递归输出子节点
        NodeIterator nodes = node.getNodes();
        while (nodes.hasNext()) {
            dump(nodes.nextNode());
        }
    }

有点无聊,也没啥好说的,就到这儿吧。

你可能感兴趣的:(无分类)