开发我们的例子程序
jackrabbit已经配置好了,现在让我们来创建我们的示例程序。这个例子程序将调用JCR-170 API。很显然,我们需要做两件事情:一个是作为后台的对数据进行增删改查(持久层),另一个是开发相对应的UI界面(WEB 层)。首先,让我们定义一个DAO接口。这个接口BlogEntryDAO.java 如下:
public interface BlogEntryDAO {
public void insertBlogEntry(BlogEntryDTO blogEntryDTO)
throws BlogApplicationException;
public void updateBlogEntry(BlogEntryDTO blogEntryDTO)
throws BlogApplicationException;
public ArrayList getBlogList()
throws BlogApplicationException;
public BlogEntryDTO getBlogEntry(String blogTitle)
throws BlogApplicationException;
public void removeBlogEntry(String blogTitle)
throws BlogApplicationException;
public ArrayList searchBlogList(String userName)
throws BlogApplicationException;
public void attachFileToBlogEntry(String blogTitle, InputStream uploadInputStream)
throws BlogApplicationException;
public InputStream getAttachedFile(String blogTitle)
throws BlogApplicationException;
}
正如你看到的,这个接口提供了增删改查的方法,同时还提供了两个方法来处理附件。接下来,我们需要一个DTO对象用来在两个层之间传递数据。
public class BlogEntryDTO {
private String userName;
private String title;
private String blogContent;
private Calendar creationTime;
//Getter and setter methods for each of these properties
}
这里我们将仅仅讨论持久层。
连接jackrabbit现在,第一件事情是开发一个组件,获得一个到jackrabbit内容仓库的连接。为了简单,我们将在程序启动的时候获得这个连接,然后在程序停止的时候释放这个连接。这里我们使用了Struts ,所以我们需要开发一个PlugIn 类。如下:
public class JackrabbitPlugin implements PlugIn{
public static Session session;
public void destroy() {
session.logout();
}
public void init(ActionServlet actionServlet, ModuleConfig moduleConfig)
throws ServletException {
try {
System.setProperty("org.apache.jackrabbit.repository.home",
"c:/temp/Blogging");
Repository repository = new TransientRepository();
session = repository.login(new SimpleCredentials("username",
"password".toCharArray()));
} catch (LoginException e) {
throw new ServletException(e);
} catch (IOException e) {
throw new ServletException(e);
} catch (RepositoryException e) {
throw new ServletException(e);
}
}
public static Session getSession() {
return session;
}
}
init()方法将会在程序启动的时候调用,destroy()将会在程序停止的时候调用。我们在init()方法里获得了到jackrabbit内容仓库的连接。看看代码,我们做的第一件事是设定了org.apache.jackrabbit.repository.home这个系统属性,在上篇文章里提到,这个属性是用来指向我们的内容仓库主目录。这里我们设定它为c:/temp/blogging。接下来,我们创建了TransientRepository的一个实例。这是jackrabbit提供的类,它提供了一个到内容仓库的代理。它在第一个session 打开的时候自动启动内容仓库,在最后一个session 关闭的时候自动关闭内容仓库。
一旦我们获得了一个内容仓库对象,我们就可以调用它的login() 方法来打开一个连接。login() 方法需要一个Credential 对象作为参数。如果Credential 对象是NULL,jackrabbit会认为其他的机制做了这个验证(比如JAAS)。login() 方法还可以传入一个workspace名字作为参数,如果不传入这个参数,jackrabbit会返回一个session对象指向默认的workspace。注意workspace和session是一对一的,即一个session仅对应一个workspace。(注:如果不传入Credential对象,返回的session对workspace是只读的)
增加内容连接已经建立起来了,下面让我们实现BlogEntryDAO这个接口。第一个我们想实现的方法是插入数据 insertBlogEntry()
public void insertBlogEntry(BlogEntryDTO blogEntryDTO)
throws BlogApplicationException {
Session session = JackrabbitPlugin.getSession();
Node rootNode = session.getRootNode();
Node blogEntry = rootNode.addNode("blogEntry");
blogEntry.setProperty("title", blogEntryDTO.getTitle());
blogEntry.setProperty("blogContent", blogEntryDTO.getBlogContent());
blogEntry.setProperty("creationTime", blogEntryDTO.getCreationTime());
blogEntry.setProperty("userName", blogEntryDTO.getUserName());
session.save();
}
首先获得session 对象,即到内容仓库特定workspace的连接。然后,我们在这个session 对象上调用getRootNode() 方法,获得这个workspace的根节点,这个根节点的路径是("/").一旦我们获得这个根节点,我们就可以通过addNode()方法在这个根节点下增加新的子节点。新节点的名字是blogEntry. 通过setProperty() 方法我们把数据存储到节点的property里。正如我们先前说明的,真实的数据是存储在property元素里,property元素是叶子。
注意session.save() 这行代码。这个方法是必须调用的,这个方法调用之前,任何 Node,Property的改变都被保存在这个session的一个临时区域里,其他的和该session连接到相同workspace的session都看不到这些改变。当这个方法被调用并被成功执行后,这些Node,Property的改变才会被持久化到这个session关联的workspace里,同时所有与这个workspace关联的session才可见这些变化。相对应的,Session.refresh(false)将会丢弃所有这些改变。item.save()和Item.refresh(false)作用相似,只是影响范围限定在单个Item上(注意,包括它的子节点)
posted @
2007-01-25 23:12 ronghao 阅读(3823) |
评论 (0) |
编辑 收藏
内容仓库模型
JSR-170 是这样定义内容仓库的,内容仓库由一组 workspace(工作空间)组成,这些workspace通常应该包含相似的内容。一个内容仓库有一个到多个 workspace。每个workspace都是一个树状结构,都有一个唯一的树根节点(root node)。树上的item(元素)或者是个node(节点)或者是个property(属性)。每个node都可以有零个到多个子节点和零个到多个子属性。只有根节点没有父节点,其余所有的节点都有一个父节点。property 也必须有一个父节点,但它没有子节点或是子属性,property 是叶子元素。property是真正存储数据的元素。
下图描述了一个blog应用程序的内容仓库模型。每个root node(根节点)的子节点都代表了一个blog实体。与这个blog实体有关的数据都存储在 bolgEntry 节点的属性里,其中一个 blogAttachment property 存储了一个二进制图片文件。
根据内容仓库实现的功能,JSR-170定义了三种级别:
Level 1:定义了一个只读的内容仓库。功能包括读取内容,将内容导出为XML和查找内容。
Level 2:定义了可写的内容仓库。Level 2是Level 1的扩展,新增的功能包括往内容仓库里写入内容,和从XML导入数据到仓库。
Advanced options:定义实现五种附加功能,版本控制、JTA、SQL查询、清晰的内容锁定和监视。
什么是Apache JackRabbit?Apache JackRabbit是一个开放源码的JSR-170 实现,实现了Level 2,但它还有许多扩展的功能。详细可以去它的官方网站。
下面我们决定用Apache JackRabbit来作为我们示例程序的内容仓库。
如何配置Apache JackRabbitJackRabbit需要两个参数来配置一个内容仓库实例。
1.内容仓库主目录:这个文件目录下通常包含了所有的内容,搜索索引,内部配置文件和其他持久化信息。它的结构看起来会像下面这个样子:
c:/temp
|
|--Blogging
|
|-repository
| |
| |-index
| |-meta
| |-namespaces
| |-nodetypes
|
|-version
|
|-workspace
|
|--default
在上面的情况下,内容仓库主目录是c:/temp/Blogging.
2.内容仓库配置文件:一个典型的配置文件如下:
<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"/>
<LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule">
<param name="anonymousId" value="anonymous"/>
</LoginModule>
</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.db.DerbyPersistenceManager">
<param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
<param name="schemaObjectPrefix" value="${wsp.name}_"/>
</PersistenceManager>
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
<param name="path" value="${wsp.home}/index"/>
</SearchIndex>
</Workspace>
<Versioning rootPath="${rep.home}/version">
<FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
<param name="path" value="${rep.home}/version" />
</FileSystem>
<PersistenceManager
class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
<param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>
<param name="schemaObjectPrefix" value="version_"/>
</PersistenceManager>
</Versioning>
<SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
<param name="path" value="${rep.home}/repository/index"/>
</SearchIndex>
</Repository>
在这个配置文件里,<Repository>元素是根元素,它包含了下面这些元素:
a,<FileSystem>: 该元素配置了内容仓库的全局数据存储位置,这些全局数据包括已注册的命名空间,定制的节点类型等等。 JackRabbit 提供了几种选择,一种是像上面例子里配置的存储在本地文件里,LocalFileSystem. 如果你想把它们存储在数据库里,你可以使用 DbFileSystem.
b,<Security>:内容仓库的安全配置,它有两个子元素:<AccessManager>和<LoginModule>。<AccessManager>配置的类用来判断用户有没有权限来对特定数据执行特定的操作。
c,<Workspaces>:这个元素的配置对所有的workspace都通用。它的rootPath 属性是所有workspace文件夹的根目录,在我们的例子里它是c:/temp/Blogging/Workspace;defaultWorkspace 属性则包含了workspace的默认名。
d,<Workspace>:这个元素是所有workspace的默认配置模板。去每个workspace文件夹下你都会发现一个workspace.xml文件,这个文件和这个元素的配置一模一样。三个子元素:<FileSystem>,和这个workspace相关数据的存储位置;<PersistenceManager> ,这个workspace内容节点存储策略;<SearchIndex>,可选,全文检索。
e,<Versioning>:配置一个版本相关的对象。其实JackRabbit也是把它作为节点来处理的。
这两个参数可以通过两种方式设置,一种是在仓库实例创建时直接传到Jackrabbit里去,一种是间接的通过设置JNDI object factory。
你可以设置org.apache.jackrabbit.repository.home 这个系统属性的值来指定你的内容仓库主目录;也可以设置
org.apache.jackrabbit.repository.conf 这个系统属性的值来指定你的内容仓库配置文件repository.xml。如果你不设定这两个
参数,Jackrabbit会把当前目录作为内容仓库主目录,同时,它有一个默认的内容仓库配置文件。
posted @
2007-01-23 23:57 ronghao 阅读(6803) |
评论 (25) |
编辑 收藏
原文地址:http://www.onjava.com/pub/a/onjava/2006/10/04/what-is-java-content-repository.html?page=4
JSR-170把自己定义为一个能与内容仓库互相访问的,独立的,标准的方式。同时它也对内容仓库做出了自己的定义,它认为内容仓库是一个高级的信息管理系统,该系统是是传统的数据仓库的扩展,它提供了诸如版本控制、全文检索,访问控制,内容分类、访问控制、内容事件监视等内容服务。
Java Content Repository API(JSR-170)试图建立一套标准的API去访问内容仓库。如果你对内容管理系统(CMS)不熟悉的话,你一定会对内容仓库是什么感到疑惑。你可以这样去理解,把内容仓库理解为一个用来存储文本和二进制数据(图片,word文档,PDF等等)的数据存储应用程序。一个显著的特点是你不用关心你真正的数据到底存储在什么地方,是关系数据库?是文件系统?还是XML?不仅仅是数据的存储和读取,大多数的内容仓库还提供了更加高级的功能,例如访问控制,查找,版本控制,锁定内容等等。
一段时间以来市场上出现了各个厂家开发的不同的CMS系统,这些系统都建立在他们各自的内容仓库之上。
问题出现了,每个CMS开发商都提供了他们自己的API来访问内容仓库。这对应用程序的开发者带来了困扰,因为他们要学习不同的开发商提供的API,同时,他们的代码也与这些特定的API产生了绑定。
JSR-170正是为解决这一问题而出现的,它提供了一套标准的API来访问任何数据仓库。通过JSR-170,你开发代码只需要引用 javax.jcr.* 这些类和接口。它适用于任何兼容JSR-170规范的内容仓库。
我们将通过一个例子来逐步了解JSR-170。
为什么需要 Java Content Repository API随着各个厂家各自的内容仓库实现数量的增长,人们越来越需要一组通用的编程接口来使用这些内容仓库,这就是JSR-170所要做的东西。它提供一组通用的编程接口来连接内容仓库。你可以把JSR-170理解为和JDBC类似的API,这样你可以不依赖任何具体的内容仓库实现来开发你的程序。你可以直接使用支持JSR-170的内容仓库;或者如果一些厂家的内容仓库不支持JSR-170则可以通过这些厂家提供的JSR-170驱动来完成从JSR-170与厂家特定的内容仓库的转换。
下面这张图描述了使用JSR-170开发的应用系统的结构。在该系统运行的时候,它可以操作内容仓库1,2,3中的任意一个。在这些内容仓库当中,只有2是直接支持JSR-170的,剩下的两个都需要JSR-170驱动来和应用系统交互。注意:你的应用系统完全不用关心你的数据是如何存储的。1可能使用了关系数据库来存储,而2使用了文件系统,至于上,它甚至更前卫的使用了XML。
JSR-170 API对不同的人员提供了不同的好处。
●对于开发者无需了解厂家的仓库特定的API,只要兼容JSR-170就可以通过JSR-170访问其仓库。
●对于使用CMS的公司则无需花费资金用于在不同种类CMS的内容仓库之间进行转换。
●对于CMS厂家,无需自己开发内容仓库,而专注于开发CMS应用。
posted @
2007-01-23 15:13 ronghao 阅读(5102) |
评论 (3) |
编辑 收藏
2006年马上就要过去,在06年的最后一天总结一下自己一年来的生活工作,自己觉得还是挺有意义的,尽管上学时最烦的就是各种各样的总结了。
工作技术上:
1、可以比较熟练的使用webwork、spring和hibernate.这是和05年做的对比,05年做的东西完全是jsp+servlet+jdbc.尽管自己那时也用过,但那还限于demo。
2、对一个不算复杂的WEB项目有比较清晰的开发思路/架构。三层、封装、抽象、领域对象。
3、有过一次和最终用户打交道的经历,有了一些经验。
4、负责开发了公司产品的权限系统。对ACL、RBAC都有了谈不上深入的理解。最后的实现是基于 acegi的扩展。包括数据范围权限、单条数据权限都实现了控制。只是对所谓的大集中模式下的权限没有一个很好的解决方法,也是一个遗憾。
5、初步的学习了ajax。05年自己对javascript还是很不屑的,也仅仅把它用于表单的验证。今年买了本权威指南,也算是浏览过一遍。在开发中用到了prototype.js,dwr,观念发生了很大的变化。写了个outlookbar.js以及把xloadtree进行了很多的扩展。最后还模仿了google的日程表也做了个日程表,比较丑,css还得加强。
6、基于jackrabbit实现了产品的CMS。对jsr170有了理解。一度想数据仓库可否替代数据库在一定应用中。
7、有了一些工作流理论和实践。
生活中:
1、世界观发生了一点变化。什么是民主,什么是人权,什么叫自由,你真的经过你自己的思考了吗?
2、关注了一些以前没有关注的声音。知道了一句话叫用脚投票。
3、关注了高房价、人民币升值、股票、基金。了解了一些财政金融方面知识,对自己每月把工资存入 银行产生了怀疑。
4、给自己配了台台式机,然后又给老婆买了台笔记本。
展望:
1、年底开始注意敏捷开发,明年能不能敏捷到我们团队?
2、很显然,ajax刚刚会用的阶段,上升到模式?我们的页面能不能xhtml规范?
3、OSGI应用到我们的产品中?
4、具有架构的能力?
5、明年我能买的起房吗?
呵呵,不管怎样,希望明年会更好。
posted @
2006-12-31 16:22 ronghao 阅读(920) |
评论 (1) |
编辑 收藏
似乎到了年末,心情也跟着莫名的烦躁起来了。CMS基本算是开发完毕,现在主要是改改产品的BUG。BUG单总是那么长,让人打不起精神。产品的发布已经几次延期,看不到海的对岸。其实还有好多的问题要去解决。想着对acegi 的ACL进行进一步的扩展,CMS的页面模板要实现可视化的编辑,对页面的javascript进行重新整理,把页面由table换成div...确实还有很多的工作。没有觉得充实而是杂乱没有条理。
也许也和生活有关。
浮躁的年末的一个傍晚,我坐在那里,看着外边的天慢慢黑透...
posted @
2006-12-29 18:30 ronghao 阅读(430) |
评论 (0) |
编辑 收藏