ApacheDS学习

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 中的目录服务

目录服务是保存和组织数据的应用程序。目录服务处理不需要频繁更新的数据,例如系统用户的个人数据(例如姓名、地址、电话号码)或者车间的生产能力(例如安装的设备的数量、型号和生产能力)。在本系列的第 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。

常见专用名词解释:

DN=Distinguished Name 一个目录条目的名字

CN=Common Name 为用户名或服务器名,最长可以到80个字符,可以为中文;

OU=Organization Unit为组织单元,最多可以有四级,每级最长32个字符,可以为中文;

O=Organization 为组织名,可以364个字符长

C=Country为国家名,可选,为2个字符长

L=Location 地名,通常是城市的名称

ST 州或省的名称

O=Orgnization 组织名称

OU=Orgnizagion Unit 组织单位

STREET 街道地址

UID 用户标识

学习用的应用程序

对于应用程序的场景,假设要为一个制造公司设计一个数据管理系统。公司有员工、客户、合作伙伴和供应商,他们都是数据管理系统的用户。现在要求数据管理系统把关于用户的数据保存在 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 中存储各种类型的数据(包括 Java 对象),需要学习 LDAP 模式。而且,用图形方式查看 LDAP 服务器中保存的数据会有帮助,在图形方式中可以用树的形式查看数据。为了这个目的,可以使用 JXplorer(或者Apache Directory Studio),这是个基于 Java 的开放源码客户端 LDAP 实现,为保存在 LDAP 服务器中的数据提供了浏览器视图。

首先需要下载 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 之后,运行 JXplorer,创建数据条目,具体过程略。

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 对象的序列化和反序列化。

定制序列化

如果应用程序的架构不允许数据成员被序列化(例如,某个数据成员属于现有的 非序列化类),那就需要在想要序列化的 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 框架允许 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 公共选项的实例,然后为使用选项的每个人保存一个引用是有好处的。

RFC2713有一个名为javaNamingReference 的对象类(图24所示),它扩展了javaObject类,并定义了两个新属性:javaReferenceAddress 和 javaFactory。javaReferenceAddress 属性类型保存 Java 对象的引用(或地址),而 javaFactory 属 性类型保存工厂对象的名称。

图 24. javaNamingReference 类

  

第 1 部分结束语

在介绍把Java对象保存到ApacheDS中的系列文章的第 1 部分,学习了很多 ApacheDS 中关于可插入协议的支持。还学习了核心 LDAP 概念和术语,包括专有名称、对象类、属性类 型和LDAP语法。最后,学习了如何用这些LDAP概念在ApacheDS 中表示和保存Java对象。

在下部分,将把这些概念放在一起测试。包含了几个示例 Java 应用程序和许多Java代 码,以演示如何在ApacheDS中保存、搜索和检索Java对象。示例应用程序实现了这里介绍的数据管理场景的各个方面。在结束第2部分之前,还利用可以用在您自己的应用程序中的可重用 Java 类的形式总结了相关概念。

应用程序1. 存储Java对象

先从几个应用程序开始,演示如何在 ApacheDS 中存储 Java 对象。出于这个目的,需要使用 Java 命名和目录接口 (JNDI),它提供了操作目录中的对象和属性的接口和方法。

  JNDI 不是特定于 LDAP 的接口,因此可以拥有针对任何目录服务类型的 JNDI 实现。如果想实现自己的目录服务并用 JNDI 公开它的功能,则需要为目录服务实现 JNDI 接口。注意,Java 2 标准版(J2SE)提供了 LDAP 的客户端 JNDI 实现,可以用它与 ApacheDS 对话。

清单 1 是一个名为 StoreAlicePreferences 的简单应用程序。我将用这个应用程序介绍如何将用户 Alice 的选项作为 Java 对象存储到 ApacheDS 中。

清单 1. StoreAlicePreferences

Public class StoreAlicePreferences{

Public StoreAlicePreferences(){

    try{

       //------------------------------------------

       //Step1: Setting up JNDI properties for ApacheDS

       //------------------------------------------

       InputStream inputStream = new FileInputStream("ApacheDS.properties");

       Properties properties = new Properties();

       properties.load(inputStream);

       properties.setProperty("java.naming.security.credentials", "secret");

       //------------------------------------------

       //Step2: Fetching a DirContext object

       //------------------------------------------

       DirContext ctx = new

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值