Apache 目录服务器是众多的 Internet 协议中的一个开放源码的、基于 Java 的实现。ApacheDS 的核心是目录服务,可以保存数据,并对不同类型的数据进行搜索操作。协议的实现在目录服务器顶层工作,提供与数据存储、搜索和检索有关的 Internet 服务。
ApacheDS 最重要的特性可能是利用不同协议公开目录服务的能力。这意味着可以把应用程序的数据(包括运行时的 Java 对象)保存在 ApacheDS 中,而不同的客户机可以使用不同的协议来利用数据。由 ApacheDS 实现的最重要的协议是轻量级目录访问协议(LDAP)。ApacheDS 充当 LDAP 服务器,侦听请求,与内部核心目录服务协调,响应 LDAP 请求。
在这个两部分的系列中,我将介绍核心的 ApacheDS 架构,并介绍在 ApacheDS 中保存运行时 Java 对象的全部步骤。因为我对 ApacheDS 的关注几乎全在它作为 LDAP 服务器实现上,所以本系列的第 1 部分主要介绍 LDAP 功能和术语。但在进入这部分之前,我要介绍 ApacheDS 模块化的可扩展架构,并解释如何用它把新的协议实现和 Internet 服务插入 ApacheDS.对 ApacheDS 核心目录服务工作方式的理解,有助于后面理解它提供 LDAP 功能的方式。
要跟上这篇文章的讨论,需要 下载并安装 ApacheDS 和 JXplorer.可能还要下载 完整源代码 供文章使用。
ApacheDS 中的目录服务
目录服务 是保存和组织数据的应用程序。目录服务处理不需要频繁更新的数据,例如系统用户的个人数据(例如姓名、地址、电话号码)或者车间的生产能力(例如安装的设备的数量、型号和生产能力)。在本系列的第 2 部分中,将介绍一个整合了这两种类型数据的示例应用程序。现在,我把重点放在 ApacheDS 提供目录服务的方式上。
ApacheDS 实现 JNDI
在图 1 中可以看到,ApacheDS 为自己的核心目录服务实现了 Java 名称和目录接口(JNDI)包装器。JNDI 是 Java 接口,定义了执行目录操作的方法,例如在目录中保存数据和搜索保存的数据。JNDI 是 Java 2 企业版(J2EE)和 Java 2 标准版(J2SE)的组成部分。其中 J2SE 只包含客户端 JNDI 支持,而 J2EE 容器通常包含服务器端 JNDI 实现。J2EE 容器可以通过 ApacheDS 的 JNDI 包装器利用它的目录服务,如图 1 所示:
图 1. ApacheDS 在 J2EE 容器内工作
JNDI 中的接口集提供了目录服务的抽象。JNDI 实现提供了与目录服务对话的实际逻辑(例如,Java 平台自带了针对 LDAP 的 JNDI 实现)。只要拥有针对这一类型的 JNDI 实现,您就可以用 JNDI 与任何类型的目录服务对话。如果想在基于 Java 的客户机应用程序中使用 JNDI,就需要 JNDI 的客户端实现。客户端 JNDI 实现提供的类,实现的是进行目录操作请求的 JNDI.
ApacheDS 实现服务器端 JNDI.这意味着它包含的类,实现的是对目录操作请求进行响应的 JNDI 接口。正如前面指出的(如 图 1 所示),J2EE 容器可以通过 ApacheDS 的 JNDI 包装器利用它的目录服务。
可插入的协议支持
图 1 只显示了 ApacheDS 的一个使用模型。ApacheDS 的用途不仅仅是嵌在 J2EE 容器内作为目录服务。可以用 ApacheDS 实现任何需要后端目录服务的协议。甚至可以用它同时为各种类型的协议服务;例如,当前的 ApacheDS 实现就同时实现了 LDAP 和 Kerberos.而且,ApacheDS 中支持的协议的列表仍在增长。
ApacheDS 拥有灵活的可扩展的架构,因此可以实现新的协议。在图 2 中可以看到 ApacheDS 架构的模型,它工作在 图 1 所示的 JNDI 包装器之上:
图 2. ApacheDS 灵活的可扩展的架构
可以看到,ApacheDS 使用一套叫做多用途网络应用程序接口(MINA)的接口。MINA 支持把新的协议实现插入 ApacheDS.在介绍这些内容之前,我要解释一下 MINA 的工作方式。
MINA 如何工作
MINA 中的接口包含的方法可以生成特定协议的工厂对象。这些工厂对象提供了把新协议实现插入 ApacheDS 的手段。协议实现负责实现 MINA 接口,ApacheDS 框架依靠 MINA 包含的方法与协议实现对话。
例如,MINA 有一个叫做 ProtocolProvider 的接口,该接口有一个 getCodecFactory() 方法。ProtocolProvider.getCodecFactory() 方法返回一个对象,公开另一个叫做 ProtocolCodecFactory 的 MINA 接口。
ApacheDS 中的协议实现,实现了 ProtocolProvider 接口。例如,ApacheDS 中的 LDAP 实现拥有一个叫做 LDAPProtocolProvider 的类,这个类实现了 ProtocolProvider 接口。
LDAPProtocolProvider 中的 getCodecFactory() 方法返回一个公开 ProtocolCodecFactory 接口的对象。这个 ProtocolCodecFactory 是一个工厂对象,ApacheDS 框架用它创建特定于 LDAP 的编码和解码对象。
ProtocolCodecFactory 包含 newEncoder() 和 newDecoder() 方法,这两个方法返回的对象公开 MINA 的 ProtocolEncoder 和 ProtocolDecoder 接口。特定于协议的编码对象公开 ProtocolEncoder 接口,而解码对象则公开 ProtocolDecoder 接口。
MINA 的编码和解码框架
猜也猜得出来,ApacheDS 框架使用特定于协议的 ProtocolDecoder 实例对协议请求进行解码,这样就可以在处理请求之前理解请求的意义。在解码之后,ApacheDS 处理请求。例如,如果请求是一个 LDAP 搜索请求,那么 ApacheDS 就会在后端目录服务中搜索需要的数据,并提取搜索结果。
在找到需要的搜索结果后,ApacheDS 框架用特定于协议的 ProtocolEncoder 对象编码搜索结果。在 LDAP 搜索请求的示例中,ApacheDS 会使用特定于 LDAP 的 ProtocolEncoder 对象在向请求客户机发送响应之前对搜索结构进行编码。
MINA 的服务框架
MINA 还有处理服务的类。任何服务提供者都可以把自己注册到一个服务注册表,而协议提供者会与提供服务的提供者类一起注册。然后协议提供者就把协议请求映射成 JNDI 操作。简单的示例就是映射到 JNDI 搜索请求的 LDAP 搜索请求。一旦 ApacheDS 框架了解了在处理协议请求时需要包含哪个 JNDI 操作,它就能生成事件。
MINA 中的事件处理框架把事件传递给适当的处理程序。例如,如果请求需要调用 JNDI 搜索操作,那么就调用搜索处理程序。MINA 还维护了一个线程池。如果处理程序正忙着处理前一个操作,那么事件会临时保存在线程池中,直到得到处理。
在 图 2 中可以看到操作 JNDI 的协议提供者、MINA 接口、类以及操作处理程序。
ApacheDS 框架最大的优势可能是它使用公共的目录服务(JNDI)处理不同的协议提供者。这意味着可以使用 ApacheDS 时,可以用不同的协议向客户机公开数据。因为 ApacheDS 支持的最重要的协议是 LDAP (而且因为日后主要会把 ApacheDS 用作保存 Java 对象的 LDAP 服务器),所以我想进一步深入讨论 LDAP.请参阅 参考资料 了解关于 ApacheDS 架构的更多内容。
LDAP 概述
LDAP 协议定义了目录操作的请求和响应消息。目录操作包括把新数据保存到目录,搜索和检索保存的数据,删除不需要的数据,更新过时数据,以及类似的操作。(请参阅 参考资料 获得 RFC 2251,这是正式的 LDAP 规范,定义了保存、检索、更新和删除 LDAP 目录中的数据的请求和响应消息。)
ApacheDS 中用来保存新数据(例如运行时 Java 对象)的 LDAP 消息叫做绑定(bind) 消息。绑定消息把用户数据传输到 LDAP 目录服务 —— 例如 ApacheDS,并把数据保存到目录。
LDAP 并不考虑数据存储的物理位置。相反,LDAP 保存在 ApacheDS 中的每个数据条目指定了专有名称(Distinguished Name,DN)。在整个目录服务中,每个 DN 都必须是惟一的。不允许有两个条目拥有相同的 DN.在本文后面将了解到 LDAP 保证每个 DN 惟一的机制。
另外,LDAP 中的搜索机制也使用 DN.下一节介绍的示例应用程序场景将让您熟悉 LDAP 的术语,并介绍 LDAP 的搜索机制。贯穿这个两部分的系列,我都将使用这个示例应用程序。
学习用的应用程序
对于应用程序的场景,假设要为一个制造公司设计一个数据管理系统。公司有员工、客户、合作伙伴和供应商,他们都是数据管理系统的用户。现在要求数据管理系统把关于用户的数据保存在 ApacheDS 中。
系统允许所有用户都可以设置自己喜欢的使用系统的方式。例如,用户可以定制在使用系统时,在个人的默认视图中显示的数据,并为不同的数据元素应用不同的显示样式。系统还支持根据用户类型 的特殊设置。例如,公司的员工(内部组织用户)可以设置消息传递选项,客户可以设置发货选项,供应商可以设置发票选项。
在 ApacheDS 中设置每个用户的设置的简单方式是以 Java 对象的形式保存选项。对于这个应用程序场景,可以从设计一个针对所有用户类型的 Preferences 类开始。Preferences 类包含的方法允许用户设置所有用户类型(在这个示例中,是内部用户、客户和供应商)公共的选项。例如,Preferences 类可能包含一个 setStyles() 方法,用于指定样式表的位置。可以用样式表给不同的数据元素应用显示样式。
也可以扩展 Preferences 类,形成 MessagingPreferences 类,用它包含针对内部用户的消息传递选项。类似地,可以为客户设计 ShippingPreferences 类,为供应商设计 InvoicingPreferences 类。
清单 1 是 Preferences、MessagingPreferences、ShippingPreferences 和 InvoicingPreferences 类的骨架。为简单起见,在清单 1 中我没有包含任何方法(除了 setStyles() 方法)。现在我只想演示类实例在 ApacheDS 中的存储。
清单 1. 代表不同类型用户的选项的 Java 类
public class Preferences implements java.io.Serializable { String styleSheetURL = null; public void setStyles(String styleSheetURL){ this.styleSheetURL = styleSheetURL; } //Other methods of the Preferences class } public class MessagingPreferences extends Preferences { //Methods of the MessagingPreferences class } public class ShippingPreferences extends Preferences { //Methods of the ShippingPreferences class } public class InvoicingPreferences extends Preferences { //Methods of the InvoicingPreferences class } |
用 ApacheDS 管理数据
假设 Alice 在制造公司的商务部门工作。她用数据管理系统把她的所有数据(姓名、部门、电子邮件地址、电话号码,等等)和她的选项(以 MessagingPreferences 对象的形式)保存到 ApacheDS 中。她保存在 ApacheDS 中的所有数据都有一个惟一的 DN。
|
现在假设 Alice 想用数据管理系统修改她的消息传递选项。数据管理系统首先用 LDAP 搜索 Alice 的命名上下文,以了解它的 DN。在知道了 DN 之后,就从 DN 提取 Alice 的 MessagingPreferences 对象,用 Alice 的最新数据更新对象,然后把对象保存回 ApacheDS。
现在脑子里有了一个应用程序场景,下面开始研究如何用 ApacheDS 变化所有这些服务器端魔术。
ApacheDS 入门
要理解如何在 ApacheDS 中存储各种类型的数据(包括 Java 对象),需要学习 LDAP 模式。而且,用图形方式查看 LDAP 服务器中保存的数据会有帮助,在图形方式中可以用树的形式查看数据。为了这个目的,我要介绍如何使用 JXplorer,这是个基于 Java 的开放源码客户端 LDAP 实现,为保存在 LDAP 服务器中的数据提供了浏览器视图。因为我使用 JXplorer 来介绍 LDAP 模式,所以在继续之前需要 下载和安装 JXplorer。
如果还没有下载 ApacheDS,那么现在还需要 下载 ApacheDS。安装相当简单:会得到一个 zip 文件,解压它得到 ApacheDS 的 JAR 文件。从命令行上,像下面这样运行 apacheds-main-0.9.jar,启动 LDAP 服务器:
<JAVA_HOME>/java -jar apacheds-main-0.9.jar |
ApacheDS 的 LDAP 服务器现在在 localhost:389 上(默认端口)侦听,准备为客户机应用程序的 LDAP 请求服务。
连接 ApacheDS
在启动了 ApacheDS 之后,请运行 JXplorer,得到图 3 所示的浏览器视图。可以看到,JXplorer 还没有连接到任何 LDAP 服务器。
图 3. 启动 JXplorer 时的第一个视图
下面,需要连接 ApacheDS。为此,使用 JXplorer File 菜单中的 Connect 命令。在连接对话框中(“Open LDAP/DSML Connection”),填写图 4 所示的值:
图 4. 连接对话框
请注意在图 4 中,主机和端口字段指定了 ApacheDS 侦听的地址。连接 ApacheDS 还需要用户名和口令。ApacheDS 自带了默认管理用户名(uid=admin,ou=system)和口令(“secret”)组合,可以用它们连接 ApacheDS.请注意,我在 User DN 字段中输入了默认用户名,在 Password 字段中输入了默认口令。
在连接对话框中输入值之后,点击 OK.JXplorer 把连接请求发送到 ApacheDS,稍等一会,就可以看到如图 5 所示的屏幕。现在连接到了 ApacheDS.
图 5. 连接到 ApacheDS 之后的第一个 JXplorer 屏幕
ApacheDS 中的默认数据
图 5 中的屏幕分成两个区,非常像 Windows 的资源管理器屏幕。左边是一个树形视图,右边显示树形视图中选中的条目的细节。
左边有三个选项卡,分别是 Explore、Results 和 Schema.Explore 选项卡用来寻找 ApacheDS 中包含的数据。用 Explore 选项卡来查看 ApacheDS 中的数据条目。Results 选项卡显示搜索结果,Schema 选项卡提供 ApacheDS 支持的模式的细节。这个讨论中的大部分地方,都将使用 Explore 和 Schema 选项卡。
请注意图 5 显示选中了 Explore 选项卡,它负责提供 ApacheDS 中包含的数据的细节。因为目前在 ApacheDS 中还没有保存任何数据,所以现在看到的是默认数据。例如,如果点击图 5 中的 admin 条目,将得到图 6 所示的屏幕,显示 ApacheDS 自带的默认管理条目:
图 6. admin 条目的细节
admin 条目代表 ApacheDS 的管理员,它的用户名和口令在 图 4 中输入。图 5 的右半边显示了 admin 条目的 HTML 视图,其中包含几个字段,例如 User Password 字段。可以在 password 字段中输入新口令,并点击 Submit 按钮,修改管理员的口令。
属性类型的说明
图 6 右侧中的每一条目实际上都是一个 LDAP 属性。LDAP 定义了许多属性类型。用 LDAP 的术语来说,属性类型被称作 attributeType.
属性是为了包含数据值(例如 Common Name 字段包含的值是 ApacheDS 管理员的名称)。对于要保存成属性值的数据,必须要定义它的许多方面。例如指定数据的编码(属性是包含文本数据还是原始二进制数据)。
使用数据的另一个重要方面是要能够搜索它。例如,可能想搜索与已知姓名或电子邮件地址的用户有关的全部数据,所以就需要指定在搜索操作中应用的匹配规则。在使用电子邮件地址时,可能想执行不区分大小写的搜索。
JXplorer 可以显示用来定义默认管理条目的属性类型。点击 图 6 显示的屏幕右侧的 Table Editor 选项卡,可以看到如图 7 所示的 Table Editor 视图:
图 7. admin 条目的 Table Editor 视图
图 7 中的表包含 “attribute type” 列和 “value” 列。可以把显示的几个类型映射到 图 6 的字段。例如,图 7 的 cn 和 sn 属性类型分别对应图 6 的 Common Name 和 Surname 字段。
这意味着数据条目中的属性拥有与之对应的属性类型。ApacheDS 支持众多的属性类型,可用于广泛的目的,而且是在不同的 Internet 标准中指定的。这篇文章讨论 ApachDS 支持的属性类型的一个子集,即用来保存 Java 对象的那些属性类型。具体来说,我将使用 cn 属性类型,这是个多用途属性类型,可以用来指定任意实体的公共名称。可以用 cn 属性类型指定在企业的商务部门工作的经理的姓名,也可以用它为 Java 对象命名。可以猜得到,sn 属性类型不属于一般目的:它只用来指定人的姓。
ApacheDS 中的对象类
既然知道了属性类型的一两件事,现在来看 objectClass 属性类型,在 图 7 中它有四个值:inetOrgPerson、organizationalPerson、person 和 top.这意味着 admin 条目包含四个对象类(object class)。
实际上,ApacheDS 中的所有数据条目都使用对象类。对象类是属性类型的集合。如果数据条目(像 图 6 或 图 7 中的 admin 条目)与对象类关联,那么条目就在对象类中包含所有属性类型。
您自己可以看到 admin 条目所使用的四个对象类中包含的属性类型。点击 图 7 左侧的 Schema 选项卡,可以得到图 8 所示的屏幕。Schema 选项卡显示 ApacheDS 支持的属性类型、对象类和数据格式的信息。
图 8. Schema 选项卡
现在展开图 8 所示屏幕左侧的 objectClasses 条目。条目展开成一个长长的、按字母顺序排列的列表,列出 ApacheDS 支持的对象类型。在列表中寻找 person 对象类型,并点击它。可以看到图 9 所示的屏幕:
定义对象类
在图 9 中,能够以字段的形式看到 person 对象类的细节。以下字段字义了对象类:
表 1. 定义对象类的字段
DESC | 提供 person 对象类的文本描述。person 对象类在 RFC 2256 中定义(请参阅 参考资料),RFC 2256 描述了 LDAP 目录服务的客户或用户使用的众多对象类和属性类型。例如,任何人都可能是 LDAP 服务的用户,所以 person 对象类定义了任何人应当有的属性(例如人的全名)。 | |
MAY | 指定 person 对象类中包含的可选属性类型列表。例如,person 对象类可选地包含 userPassword 和 telephoneNumber 属性类型,它们用来容纳人的口令和电话号码。在本文后面,我将介绍如何指定属性类型的细节。 | |
MUST | 指定 person 对象类必须拥有的属性类型。person 对象类必须包含的属性只有两个:sn 和 cn,前面已经介绍过它们。 | |
NAME | 指定对象类的名称。 | |
OID | 指定对象类的对象标识符。每个 LDAP 对象类和属性类型都必须有一个惟一的对象标识符。RFC 2256 指定了 person 对象类的对象标识符。为了让对象标识符惟一,Internet Assigned Numbers Authority(请参阅 参考资料)免费发放对象标识符。对于这篇文章来说不需要获取对象标识符,因为我们没有定义任何新的属性类型。 | |
SUP | 指定对象类的双亲。对象类中的继承概念(把双亲的能力扩展到子女)与面向对象编程语言中的继承类似。请注意,根据 图 9 中的 SUP 属性值,person 对象类扩展了名为 top 的类,这个类是所有对象类的超类。这意味着 LDAP 中的所有对象类都直接或间接地扩展了 top 对象类。 |
现在来看图 10 中显示的 top 对象类,它只包含一个名为 objectClass 的属性类型。因为所有对象类都继承自 top 类,所以不论数据条目使用什么数据类型,在所有数据条目中,都存在 objectClass 属性类型。简单来说,这意味着所有数据条目都应当定义它们使用的对象类。
图 10. top 对象类
对象类的类型
对象类有三种类型:抽象、结构性和辅助性。了解了这点,就能注意到 top 对象类是抽象的,这意味着它的存在只是为了让其他对象类能够扩展它。没有数据条目会直接使用抽象类。
另一方面,person 对象类是结构性的。所有数据条目都使用结构性类。结构性类扩展其他结构性类和抽象类。例如,叫做 organizationalPerson(在 RFC 2256 中定义)的结构性类扩展 person 对象类,后者又扩展 top 对象类。
organizationalPerson 对象类代表特殊的一类人,即在组织中工作的人。所以,它定义了可以适用于组织中工作的人的属性(例如,办公室人员的工作头衔)。图 11 显示了 JXplorer 屏幕中的 organizationalPerson 对象类:
图 11. organizationalPerson 对象类
辅助对象类是为了满足一些非常特定的目的。这意味着辅助对象类不包含几乎所有数据条目都需要的通用目的属性类型(例如前面讨论的 cn 和 objectClass 属性类型)。
数据条目不能完全依靠辅助对象类。所以,使用辅助对象类的数据还必须使用至少一个结构性类。在本文后面,在开始介绍如何在 ApacheDS 中保存 Java 对象时,我将提供一个辅助类的示例。
ApacheDS 中的属性类型
现在让我们进一步考虑 ApacheDS 中属性类型的使用。请展开 图 8 中所示的 JXplorer 屏幕中的 attributeTypes 条目,它会显示许多属性类型。这些属性类型是由 ApacheDS 支持的多个规范定义的(例如 RFC 2713,它定义了在 LDAP 目录中保存 Java 对象的属性类型和对象类;请参阅 参考资料)。
图 12. ApacheDS 支持的属性类型
点击图 12 所示的 cn 属性条目,可以看到与 图 13 所示相似的屏幕。图 13 显示了定义属性类型的字段:
图 13. cn 属性类型
从前面对 person 对象类的讨论,您已经了解了上面显示的字段中的几个,所以现在只讨论那些还没了解的字段:
表 2. 更多字段
EQUALITY | 指定用具体的属性值搜索数据条目时要应用的匹配规则。对于 cn 属性类型,这个字段的值是 caseIgnoreMatch。使用这个属性类型和值意味着在使用具体名称搜索一个人时,搜索操作不区分大小写。 |
SUBSTR | 与 EQUALITY 字段类似,区别是 SUBSTR 字段指定的匹配规则,针对的是查找指定子串的搜索操作,而不是完整属性值的搜索操作。我会在本系列的 第 2 部分 演示 SUBSTR 的影响。 |
还请看 图 13 中的 SYNTAX 字段,它包含一个 OID 值。OID 值表明属性值的语法(或数据格式)。每个属性类型都以属性值的形式定义被保存的数据的语法。图 13 中的 OID 指向的 LDAP 语法,用来在 LDAP 目录中保存字符串。下一节讨论 LDAP 语法。
ApacheDS 中的 LDAP 语法
如果点击 JXplorer 的 Schema 选项卡,然后点击选项卡中的 ldapSyntaxes 条目,可以看到按字母顺序排列的 ApacheDSentry 中支持的 LDAP 语法列表。请找到名为 Directory String 的语法并点击它。可以看到图 14 所示的屏幕,显示了定义 LDAP 语法的字段:
图 14. Directory String 语法
Directory String 语法在 LDAP 目录中保存字符串值。理解所有字段的含义很容易,因为它们与前面解释过的字段相似。但是,请注意,图 14 中的 OID 字段与 图 13 中的 cn 属性的 SYNTAX 字段确切地匹配。这是因为 cn 属性类型遵守 Directory String LDAP 语法。这意味着 LDAP 把名称当作字符串。
还请看图 15,它显示了另一个 LDAP 语法,叫做 Octet String,它代表 8 进制字符串。Java 对象保存为 8 进制字符串,所以在本系列文章中将使用 Octet String LDAP 语法。
图 15. Octet String 语法
脑子里有了 Directory String 和 Octet String 语法,就可以进行下一步,用 ApacheDS 制作数据条目了。
在 ApacheDS 中输入数据
现在,已经了解了足够的 LDAP 组件以及如何在 ApacheDS 中实现它们以把新的数据条目写入 ApacheDS.这个练习有助于理解模式组件(例如对象类、属性类型和语法)如何在 LDAP 目录中保存或表示数据条目。
先从点击 JXplorer Explore 选项卡开始,然后点击选项卡中的 Users 条目。ApacheDS 没有任何默认用户,所以 Users 条目为空。右击条目,可以看到弹出菜单。在弹出菜单中选择 New 命令,得到名为 “Set Entry Object Classes” 的对话框,如图 16 所示:
图 16. Set Entry Object Classes 对话框
需要做的第一件事是在对话中提供一些信息,这些将在下一节讨论。
使用专有名称语法
正如我在讨论 LDAP 模式开始时提到过的,ApacheDS 中的所有数据都以树的形式存在。这意味着除了根条目之外,每个数据都拥有双亲。ApacheDS 的根条目名为 system,在 图 16 中可以看到。
每个数据条目都拥有一个在整个目录中都惟一的 DN.RFC 2253 (请参阅 参考资料)提供了以字符串形式编写 DN 的语法。根据 RFC 2253,DN 是用逗号分隔的组件列表的形式编写的,每个组件都有自己的值。虽然可以用多个组件定义一个 DN,但我在本文中只使用三个组件:ou、uid 和 cn.每个组件都有自己的目的:
ou 组件指定组织单元的名称。对于本文,可以想像它实际是数据组织单元的名称。用这个属性可以在 ApacheDS 中构建不同类型的数据。
uid 组件提供条目的用户标识符。正常情况下,用这个属性标识 ApacheDS 的用户(例如 Alice)。
cn 组件命名 Java 对象。
图 16 中所示的根条目 system 的 DN 是 ou=system.“users” 条目是 system 的直接孩子,所以 user 的 DN 是 ou=users,ou=system.请注意 user 的 DN 的第一部分(ou=users)叫做它的相对专有名称(RDN)。
创建 RDN 条目
子条目(例如 ou=users)的 RDN 前面要加上双亲的 DN (例如 ou=system),中间用逗号分隔,从而构成子条目的 DN(例如 ou=users,ou=system)。同一双亲的两个相邻孩子(即兄弟)不会有相同的 RDN.这就保证了在整个 LDAP 目录中 DN 都是惟一的。
记住了这一点,就可以容易地理解 图 16 所示的 Parent DN 和 Enter RDN 字段的含义了。在创建新条目时,右击 Users 条目,出现 Parent DN 字段,里面已经填充了 Users 条目的 DN.
下一步是提供想要创建的条目的 RDN.对于示例应用程序,要创建名为 Alice 的新用户,所以在 图 16 的 Enter RDN 字段中输入 uid=Alice .这就形成了 Alice 条目的 DN uid=alice,ou=users,ou=system.
请注意对于专有名称的数据查询或搜索操作(我将在本系列的第 2 部分演示)是不区分大小写的。这意味着从实践上看,指定 uid=Alice 或 uid=alice 作为 RDN 字段的值是没有区别的。
使用对象类
迄今为止都还不错。下面,选择新条目要使用的对象类。可以为一个条目选择任意数量的对象类。在选择对象类时,需要提供具体对象类中包含的所有必需属性。另外,还要提供对象类的可选属性。
应当记得 Alice 在数据管理系统的目标公司的商务部门工作,所以她只需要属于 organizationalPerson 对象类的属性,这些属性我在 图 11 中介绍了。所以,请把 organizationalPerson 对象类加入 图 16 所示的 Selected Classes 列表,并点击 OK.
请看图 17 的屏幕,其中在 Set Entry Object Classes 对话框之后显示了 Alice 条目。请注意图 17 中 Alice 的 uid 值(刚刚输入的)用蓝色显示:
图 17. Alice 条目显示在 Set Entry Object Classes 对话框之后
应用属性值
下面,需要提供必需属性的值(cn 和 sn 属性,在 图 15 中以黑体显示),并点击 Submit.然后,JXplorer 就会制作一个 LDAP 请求,形成一个新条目,并把请求发送给 ApacheDS.
ApacheDS 把确认响应消息发送回 JXplorer,JXplorer 更新数据树,如 图 16 所示。在图 18 的右侧可以看到生成的名为 Alice 的条目:
图 18. Alice 的条目
出现这个条目,就证明完成了。已经用 ApacheDS 完成了第一个数据条目!
Java 序列化和 RMI
只要想把 Java 对象保存在目录中,要做的第一件事就是把 Java 对象转换成字节表示形式。这种转换过程叫做序列化。序列化过程的输出是一个字节流,字节流可以通过网络传递给 ApacheDS,然后在 ApacheDS 中保存为 Java 对象的字节表示。
远程方法调用(Remote Method Invocation,RMI)用特殊的方式利用序列化过程。在讨论把 Java 对象保存在 ApacheDS 的具体细节之前,先要确保理解其中包含的概念基础。
序列化 Java 对象
Java 语言有一个规范,叫做 Java Object Serialization Specification version 1.5,这个规范定义了把 Java 对象转换成字节流的过程(请参阅 参考资料)。根据 Java Object Serialization 规范,序列化 Java 对象的过程已经在 JRE 中以序列化运行时的形式实现了,序列化运行时处理 Java 对象的序列化和反序列化。
|
序列化运行时包含两个方法:writeObject() 和 readObject()。序列化运行时根据 Java Object Serialization 规范,用它们序列化和反序列化 Java 对象。想要序列化的 Java 对象只需要实现叫做 java.io.Serializable 的接口。Serializable 接口不包含任何方法。它的作用只是告诉序列化运行时这个对象是可以序列化的。
请注意序列化运行时会在序列化过程中把 Java 对象的数据成员序列化。如果某些数据成员本身还是另外的 Java 对象,那么需要与想要序列化的主对象一起序列化这个成员。所以,这类数据成员也应当实现 Serializable 接口。
在本系列的第 2 部分,我将演示如何用 Java 中默认的序列化支持来在 ApacheDS 中保存 Java 对象。
RMI
除了序列化对象,可能想在通过网络发送字节表示之前,在它上面应用一些特定的编码方案。这个编码过程叫做编组(marshalling),它是 Java RMI 规范的重要元素。RMI 框架允许 Java 应用程序使用远程对象。远程对象位于 Java 应用程序的范围之外,例如 Internet 上。
使用 RMI,可以像调用本地对象的方法一样调用远程对象的方法。要做到这点,需要一个远程对象的存根类。存根类包含远程对象的方法签名。应用程序实例化存根类并在本地调用它的方法。然后 RMI 框架管理与远程对象的通信,形成对所需方法的远程调用。结果是,应用程序不需要了解对象实际上是在本地还是远程。
RMI 框架的细节超出了本文的范围,可以研究 参考资料 一节中的参考了解关于它的更多内容。对这个讨论有意义的内容是(您可能猜得到) RMI 框架允许 Java 对象在网络上传递,调用远程对象的方法。所以,RMI 需要序列化 Java 对象。RMI 规范指定编组算法,并把算法应用到 Java 对象的序列化形式上。算法会在序列化的对象上插入特殊标志。
RMI 规范在 J2SE 中已经实现,所以不需要担心对象编组的低层细节。只要使用 RMI 类编组 Java 对象即可。LDAP 允许把编组的 Java 对象保存在 LDAP 目录。在本系列的第 2 部分,我将演示如何编组 Java 对象和取消编组,以及如何在 ApacheDS 中保存和检索它们。
把 Java 对象保存在 ApacheDS
在本系列的第 2 部分中,我要介绍一个简单的应用程序,这个程序包含目前学到的所有概念。我将介绍如何表示序列化的 Java 对象,如何使用不同的对象类,如何表示编组的 Java 对象,如何把 Java 对象引用保存在 ApacheDS 中。在 第 2 部分 中,我将在这些简单练习上再进一步。但是现在,只需把注意力放在包含的基本步骤上。
开始时,需要一套要处理的对象类。RFC 2713(请参阅 参考资料)定义了在 LDAP 目录中表示 Java 对象的对象类和属性类型。ApacheDS 对这些属性类型和对象类提供了完整支持。在这个练习和第 2 部分的练习中,我使用了四个对象类:javaContainer、javaObject、javaSerializedObject 和 javaNamingReference.现在来看前两个对象类和它们的属性。
javaContainer(如图 19 所示)是个构造性类,只包含一个属性: cn.这个对象类的目的是命名包含 Java 对象的数据条目。LDAP 允许使用另一个结构性对象类给 Java 对象命名。例如,也可以用 person 对象类保存 Java 对象。
图 19. javaContainer 类
javaObject 是个抽象类。这个对象类的目的是定义一些辅助属性,这些属性在保存 Java 对象的数据时不是直接有用,但是从目录进行搜索或检索对象的搜索操作时会有帮助。图 20 显示了 javaObject 类:
图 20. javaObject 类
图 20 中显示的 javaObject 类中的属性类型如下所示:
一个必需的属性类型,名为 javaClassName,它容纳保存在 ApacheDS 中的实例所属的类的名称。通常用这个属性查找在 ApacheDS 中保存的具体类的实例。
另一个名为 javaClassNames 的属性类型容纳 Java 对象的所有超类以及对象实现的所有接口的名称。这个属性会包含大量的值,所以是一个多值属性类型。LDAP 允许用多值属性容纳多个值。可以用 javaClassNames 属性类型搜索实现特定接口的对象或者扩展特定类的对象。
图 20 中显示的 javaCodebase 属性类型保存 Java 对象的类定义所在的位置。这是个可选属性,如果应用程序需要从 ApacheDS 读取类的位置以便装入类定义的时候才用这个属性。
javaDoc 属性类型也是可选的,容纳 ApacheDS 中保存的类实例所属的类的 Java 文档的 URL.可以猜到,description 属性类型保存类的文本描述。
代表序列化的 Java 对象
请注意,属性类型 javaObject 中没有一个属性类型保存代表被保存对象的实际八进制字符串。这就是为什么 javaObject 类保持抽象的原因,也是为什么不能直接用它创建数据条目的原因。LDAP 用这种方式定义 javaObject 类,是因为 LDAP 允许用不同形式保存 Java 对象(例如,序列化形式的对象或编组形式的对象)。每种对象代表形式都有自己的对象类,扩展自 javaObject 类。
名为 javaSerializedObject 的对象类(图 21 所示)扩展了 javaObject 类,只添加了一个叫做 javaSerializedData 的属性。javaSerializedData 属性包含 Java 对象实际的序列化形式。
图 21 显示的 javaSerializedObject 类是个辅助对象类,至于原因马上就会解释:
图 21. javaSerializedObject 类
现在请看图 22,这是一个叫做 MessagingPreferences 的序列化 Java 对象,保存在 ApacheDS,如 JXplorer 的屏幕所示:
图 22. 保存在 ApacheDS 中的 Alice 的 MessagingPreferences 对象
图 22 中的 MessagingPreferences 对象代表用户 Alice 想使用的选项设置。这是 MessagingPreferences 对象看起来是 Alice 数据条目对象(在本文前面创建的)的子对象的原因。
使用不同的对象类
如图 22 所示,MessagingPreferences 数据条目对象使用四个对象类:javaSerializedObject、javaObject、javaContainer 和 top.您能猜出 MessagingPreferences 对象为什么需要这四个类么?
MessagingPreferences 数据条目对象需要 javaSerializedObject 类的原因是要保存 MessagingPreferences 对象的序列化形式。
需要 javaObject 类是因为 javaSerializedObject 类扩展了 javaObject 类。这意味着只要数据条目对象使用 javaSerializedObject 类,也就会使用 javaObject 类。
需要 javaContainer 类是因为其他三个对象类(javaSerializedObject、javaObject 和 top)都不是结构性类。ApacheDS 中的每个数据条目对象都需要使用一个结构性类。Java 对象可以使用 javaContainer 或其他任何结构性类(例如 person 对象类)。LDAP 希望把 Java 对象条目与结构性类(像 javaContainer 或 person 对象类)混合,所以 LDAP 规范把 javaSerializedObject 类定义成辅助类。
最后,MessagingPreferences 数据条目对象需要 top 类是因为所有对象类都扩展自 top 类,如我前面解释的。
表示编组的 Java 对象
现在看图 23,图中显示了 ApacheDS 中保存的编组 Java 对象:
图 23. ApacheDS 中保存的编组对象
可以比较图 23 和 图 22(图 22 在 JXplorer 屏幕中显示序列化对象)。图 23 中的 javaClassName 属性显示了包含 java.rmi.MarshalledObject 类的实例的条目。在本系列的第 2 部分,我将用这个类演示 Java 对象编组。
保存 Java 对象的引用
最后,LDAP 还允许在目录中保存 Java 对象的引用 而不是实际的对象。在这种情况下,把 Java 对象的地址保存在 ApacheDS 中。在取消地址引用以获得对象实例时,需要工厂对象。工厂对象实现取消引用逻辑。
保存引用的主要优势是在不同用户之间共享对象。例如,回想一下我在前面的讨论中与 清单 1 一起介绍的 MessagingPreferences 类。如果多个用户共享共同的消息传递选项,就会发现保存一个表示 MessagingPreferences 公共选项的实例,然后为使用选项的每个人保存一个引用是有好处的。
RFC 2713 有一个名为 javaNamingReference 的对象类(图 24 所示),它扩展了 javaObject 类,并定义了两个新属性:javaReferenceAddress 和 javaFactory.javaReferenceAddress 属性类型保存 Java 对象的引用(或地址),而 javaFactory 属性类型保存工厂对象的名称。
图 24. javaNamingReference 类