Sun JNDI教程翻译 第二部分 Preparations

来源:互联网 发布:excel找出相同数据 编辑:程序博客网 时间:2024/04/30 07:15
本文是JNDI Tutorial系列文章的第二部分:The Basics,介绍了JNDI的一些基础知识,诸如Naming操作和Directory操作。介绍了如何通过编程的方式访问命名和目录服务,如何使用JNDI和目录进行交互。从准备环境到查找对象以及在目录中进行搜索等操作。
1.        预备知识
在开始之前,需要完成两件事情,其一就是确保已经获取需要的软件,关于需要的软件,在下面会列举出来。另外就是,必须做一些编程设置。包括如何创建初始上下文以及如何处理JNDI抛出的异常。除此之外,还有一部分解释了如何执行教程中的例子程序。例子程序用来引导learners建立自己的应用程序。可以通过运行例子来查看描述的行为。当编写自己的程序时,当在本机上运行时,看到的结果可能和例子程序有多不同。
下面提到的软件是必须的:
Java平台软件
需要的JDK版本在v1.1.2以上即可。估计现在大多数用户应该至少使用Java 2 SDK 1.4了吧,越来越多的用户在使用1.5甚至1.6,所以基本不会需要使用扩展的JNDI。为了运行applets,可以使用任何兼容Java的Web浏览器,例如HotJava,Internet Explorer5(and later),Netscape Comunicator或者Navigator v4。
JNDI软件
从Java 2 SDK,v1.3开始,JNDI类库就已经包含在JDK中了。如果使用较早的版本,可以从http://java.sun.com/products/jndi网站下载所需的软件包。
服务provider软件
JNDI API是通用的API,可以访问任何命名和目录服务。实际的访问一个命名或者目录服务时,需要将服务provider插入到JNDI中。一个服务provider就是将JNDI API映射到实际的命名和目录服务器的调用。典型的,服务provider和命名/目录服务器的角色是不同的,从客户端和服务器的角度来说,JNDI和服务provider是客户端,叫做JNDI客户端,而命名/目录服务器是服务器。客户端和服务器会以多种形式进行交互,一种普通的方式是,它们使用一个网络协议,这样客户端和服务器可以共存在一个网络环境中。服务器通常支持多种客户端,而不仅仅是JNDI客户端,只需要客户端遵守特定的协议。JNDI对JNDI客户端和服务器的交互不作任何形式上的规定。一个极端的例子是客户端和服务器可以是同一个实体。需要获取使用的服务provider的类文件,例如,如果打算使用JNDI访问LDAP目录服务器,那么就需要一个LDAP服务provider软件。Java 2 SDK v1.3包含了LDAP、COS命名和RMI注册的服务provider。如果使用一个更早的JDK,需要获取服务provider,下载的地址依然是JNDI Web Site。
本教程使用以下两个服务provider:
¨         文件系统服务provider,用于命名实例
¨         LDAP服务provider,用于目录和事件通知实例
当使用文件系统服务provider的时候,不需要建立一个服务器,因为可以使用本地文件系统作为服务器,当使用LDAP服务provider的时候,需要创建一个自己的服务器或者可以访问一个既存的服务器。
命名和目录服务器软件
在获取了服务provider软件之后,就需要创建或者访问相应的命名和目录服务器了。创建一个命名或者目录服务器是典型的网络系统管理员的工作。不同的供应商的命名和目录服务器的安装过程是不同的。一些服务器在安装的时候要求特殊的机器权限。所以,在安装的时候需要查看安装手册。
在本教程的命名例子中,需要使用文件系统。
在本教程的目录例子中,需要访问一个LDAP服务器,一个免费的,公开的LDAP服务器是OpenLDAP,可以下载该LDAP服务器软件。一些公开的可以访问的ldap服务器如下:
ldap://ldap.Bigfoot.com
ldap://ldap.four11.com
ldap://ldap.InfoSpace.com
笔者使用的软件为开源的ApacheDS目录服务器,即Apache Directory Server,结合Apache Directory Studio(或者安装Eclipse插件,其工具本身是基于Eclipse)ldp.exe(Microsoftldp客户端工具)或者ldapbrowser工具来处理目录服务器相关的知识。关于ApacheDS,请参考笔者的另外ApacheDS系列文章及Apache官方网站的介绍(http://directory.apache.org)
2.        Contents of the Directory目录的内容
一旦安装好目录后,或者已经可以在程序中和目录通信后,就可以从目录中获取两种类型的信息:绑定和属性
目录可以被看作是由名称到对象的绑定组成的。也就是说,每个目录中的对象都有一个对应的名称。可以通过指定名称来查找对应的对象。如果在使用诸如文件系统的命名服务的话,那么文件就是对象,它们被绑定到文件名。
同时存储在目录中的还是属性,一个目录中的对象,除了有名称之外,还包含一个可选的属性集合。可以从目录中获取对象的属性,也可以通过指定特定的属性来查询对象。
这部分教程提供了访问绑定和属性的例子,关于可以从命名和目录服务中取得的细节和具体的服务相关。
Directory Schma目录模式
一个模式指定了目录中包含的对象的类型。本教程中,目录包含实体,其中一些实体需要特殊的模式定义。为了实现这些实体,必须关闭服务器中的模式检查或者向服务器中添加和本教程对应的模式文件。这些工作是由目录服务器管理员来完成的。
本教程使用到了以下两个模式文件,必须安装在服务器中:
¨         Java对象模式
¨         CORBA对象模式
这些文件的格式具有正式的描述,并且一般不可以直接拷贝粘贴到服务器的配置文件中。特别是RFC 2252中描述的属性的语法。
在本教程中的java.schema文件和corba.schema文件中定义的内容在ApacheDS中已经包含了这两个schema,并且包含了一些更多的schemaApacheDS在启动时加载的schema有如下的schemas
        <bean class="org...schema.bootstrap.AutofsSchema"/>
        <bean class="org...schema.bootstrap.CorbaSchema"/>
        <bean class="org...schema.bootstrap.CoreSchema"/>
       <bean class="org...schema.bootstrap.CosineSchema"/>
        <bean class="org...schema.bootstrapSchema"/>
        <bean class="org...schema.bootstrap.CollectiveSchema"/>
        <bean class="org...schema.bootstrap.InetorgpersonSchema"/>
        <bean class="org...schema.bootstrap.JavaSchema"/>
        <bean class="org...schema.bootstrap.Krb5kdcSchema"/>
        <bean class="org...schema.bootstrap.NisSchema"/>
        <bean class="org...schema.bootstrap.SystemSchema"/>
        <bean class="org...schema.bootstrap.ApachednsSchema"/>
其中导入的java.schema文件和corba.schema文件的内容如下(只截取注释说明部分):
#java.schema
# 3 Attribute Type Definitions
#
#    The following attribute types are defined in this document:
#
#        javaClassName
#        javaClassNames
#        javaCodebase
#        javaSerializedData
#        javaFactory
#        javaReferenceAddress
#        javaDoc
#
# 4 Object Class Definitions
#
#    The following object classes are defined in this document:
#
#        javaContainer
#        javaObject
#        javaSerializedObject
#        javaMarshalledObject
#        javaNamingReference
 
#corba.schema
# 3. Attribute Type Definitions
#
#    The following attribute types are defined in this document:
#
#        corbaIor
#        corbaRepositoryId
#
 
# 4. Object Class Definitions
#
#    The following object classes are defined in this document:
#
#        corbaContainer
#        corbaObject
#        corbaObjectReference
这里的java.schemacorba.schema文件都比Sun JNDI Tutorial提供的少了两个,一个为
( 2.5.4.13
 NAME 'description'
 EQUALITY caseIgnoreMatch
 SUBSTR caseIgnoreSubstringsMatch
 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024}
)
另一个是:
( 2.5.13.5
 NAME 'caseExactMatch'
 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
)
           description属性类型在其他的schema(ApacheDS)已经定义,但是caseExactMatch不知道具体是做什么的,ApacheDS中也没有找到相关的定义。有待考察!
不同的目录服务器配置这些模式的方法不同,本教程包含了一些安装Java和CORBA模式的工具,用于将模式安装在允许模式被LDAP进行修改的目录服务器上。也包含一些工具用于更新一个既存的目录,但是该目录包含一些老版本的模式,下面是一个工具可以完成的任务列表:
创建Java Schema
创建CORBA Schema
更新一个使用老版本Java Schema的目录中的实体
更新一个使用老版本CORBA Schema的目录中的实体
              根据相应的README文件来运行这些程序。
Providing Directory Content for This Tutorial为本教程提供目录内容
运行Setup程序来创建文件系统名称空间。这个程序创建了一个文件树,根据需要列表和查询对象的需要,创建了一个普通的参考。运行这个程序的时候,需要设置一个测试的名称空间。例如:
java Setup /tmp/tutorial,在windows系统中,使用d:/workspace/JNDITutorial/tmp/tutorial。
创建完成的目录结构如下图所示:
在d:/workspace/JNDITutorial/tmp/tutorial目录中创建了上图所示的目录,并在一些目录中填充了文件。
在本教程中的目录例子中,通过使用配置文件(tutorial.ldif)来创建LDAP目录。如果在使用一个既存的服务器,可能会看到不同的结果,在向服务器中加载tutorial.ldif文件之前,必须先跟随指南进行服务器schema的更新。(在ApacheDS中这部分已经更新完成)
首先参照笔者的关于ApacheDS的相关文章,创建分区suffix为o=JNDITutorial的分区作为root命名上下文,然后通过使用ApacheDS Tool或者Studio Tool导入ldif文件。为了保持文档的完整性,笔者简要介绍上述步骤。
首先修改$ApacheDS_HOME/conf/server.xml文件,找到如下元素后进行修改:
    <property name="contextPartitionConfigurations">
      <set>
        <ref bean="examplePartitionConfiguration"/>
        <ref bean="sevenSeasPartitionConfiguration"/>
        <ref bean="JNDITutorialPartitionConfiguration"/>
      </set>
    </property>
然后拷贝<bean id="sevenSeasPartitionConfiguration"元素的内容,并粘贴,在这个基础上进行如下的修改,修改后的结果如下:
<bean id="JNDITutorialPartitionConfiguration" class="org...MutableBTreePartitionConfiguration">
    <property name="name" value="JNDITutorial" />
    <property name="cacheSize" value="100"/>
    <property name="suffix" value="o=JDNITutorial" />
 
    <!-- the optimizer is enabled by default but may not always be what     -->
    <!-- you want if your queries are really simple                         -->
    <property name="optimizerEnabled" value="true" />
 
    <!--
      Synchronization on writes does not wait for synch operations
      to flush dirty pages. Writes persist immediately to disk at
      a cost to performance with increased data integrity. Otherwise
      the periodic synch operation will flush dirty pages using the
      synchPeriodMillis parameter in the main configuration.
    -->
    <property name="synchOnWrite" value="true" />
    <property name="indexedAttributes">
      <set>
        <bean class="org...MutableIndexConfiguration">
          <property name="attributeId" value="o" />
          <property name="cacheSize" value="100" />
        </bean>
        <bean class="org...MutableIndexConfiguration">
          <property name="attributeId" value="ou" />
          <property name="cacheSize" value="100" />
        </bean>
        <bean class="org...MutableIndexConfiguration">
          <property name="attributeId" value="krb5PrincipalName" />
          <property name="cacheSize" value="100" />
        </bean>
        <bean class="org...MutableIndexConfiguration">
          <property name="attributeId" value="uid" />
          <property name="cacheSize" value="100" />
        </bean>
        <bean class="org...MutableIndexConfiguration">
          <property name="attributeId" value="objectClass" />
          <property name="cacheSize" value="100" />
        </bean>
      </set>
    </property>
    <property name="contextEntry">
      <value>
        objectClass: top
        objectClass: organization
        objectClass: extensibleObject
        o: JNDITutorial
      </value>
    </property>
 </bean>
重新启动服务器,确认启动成功,然后再Eclipse(已经安装ApacheDS插件)中设置连接到LDAP服务器的参数如下:
点击OK,由于此时LDAP中还没有数据,所以左侧的LDAP Browser显示结果如下(只有一个根实体):
通过ldapbrowser查看结果如下:
最后导入tutorial.ldif文件,选中JNDITutorial节点,右键选择导入,如图所示:
 
注意:Sun JNDITutorial中说明,在tutorial.ldif中以distinguished name:”o=JNDITutorial”作为根命名上下文,如果使用的目录服务器不是这样的话,可能会发生错误,但是由于笔者已经使用ApacheDS创建了o=JNDITutorial这个根上下文,所以需要修改一下tutorial.ldif文件的内容,修改后的文件内容如下:
#删除蓝色的部分
dn: o=JNDITutorial
o: JNDITutorial
objectclass: top
objectclass: organization
 
dn: ou=Groups, o=JNDITutorial
ou: Groups
objectclass: top
objectclass: organizationalunit
......
导入完成的结果如图所示:
至此创建LDAP root naming context完成,由于ApacheDS默认情况下有些检索的权限设置,所以需要进行修改,关于修改的过程,请参考笔者的其他文章。
 
包和类路径
为了在程序中使用JNDI,需要完成以下编译和执行环境的配置。
引入JNDI类。以下是JNDI package:
    * javax.naming(in the API reference documentation)
    * javax.naming.directory(in the API reference documentation)
    * javax.naming.event(in the API reference documentation)
    * javax.naming.ldap(in the API reference documentation)
    * javax.naming.spi(in the API reference documentation)
在本教程中使用到的类和接口都属于前两个package。在使用的时候需要单个引入需要的类或者接口,或者将两个package全部引入,例如:
import javax.naming.*;
import javax.naming.directory.*;
编译环境。在编译JNDI程序时,需要访问JNDI类,Java2 SDK v1.3已经包含了JNDI类,如果使用的是以前的JDK版本,那么需要进行JNDI的配置。(笔者认为现在基本上很少有使用1.3版本以前的JDK,所以这部分就不翻译了)
运行环境。运行JNDI程序时,需要可以访问JNDI类以及程序用到的服务提供商的类。Java 2 Runtime Environment(JRE) v1.3已经提供了JNDI类和LDAP、COS命名、RMI注册的服务供应商的类。如果使用的是其他的服务供应商,那么需要下载特定的档案文件(jar/zip),并将其放在JAVA_HOME/jre/l ib/ext目录,其中JAVA_HOME是包含JRE的目录。同样,如果使用的老版本的JRE,那么需要在JNDI Web网站下载JNDI类。该网站同时提供了一些其他的服务provider,可以下载或者使用其他供应商的provider。
如果使用的JRE版本为JRE v1.2,那么需要按照Java扩展方法来安装JNDI类,具体方法参考本文末尾部分附录。
Naming Exceptions
当请求的操作不能被处理时,很多JNDI package中的方法抛出一个NamingExcetpion异常。通常情况下需要使用try—catch来抛出---捕获异常。
try {
    Context ctx = new InitialContext();
    Object obj = ctx.lookup("somename");
} catch (NamingException e) {
    // Handle the error
    System.err.println(e);
}
异常类继承层次。在JNDI中,有很多类继承NamingException这个异常类,异常类的名字是自说明的,即异常类的名字说明了异常的种类。如果需要对NamingException类的特定子类进行处理的话,可以单独的捕获子类,例如下面的代码片断中特殊的处理了AuthenticationException这个异常类。
try {
    Context ctx = new InitialContext();
    Object obj = ctx.lookup("somename");
} catch (AuthenticationException e) {
    // attempt to reacquire the authentication information
    ...
} catch (NamingException e) {
    // Handle the error
    System.err.println(e);
}
枚举。诸如Context.list()和DirContext.search()这样的操作返回一个NamingEnumeration对象。在这些情况下如果发生异常并且没有检索结果返回的话,将抛出一个NamingException或者其适当的子类。如果发生异常但是返回了结果,那么将返回一个NamingEnumeration对象,以便获取那些结果。当所有结果都获取后,调用NamingEnumeration.hasMore()会抛出NamingException或其子类来提示发生了错误。此时enumeration已经失效,任何对其的调用都会发生错误。
例如,如果执行search()方法,并指定返回结果的限制为n,这是检索结果返回的enumeration最多包含n个结果。如果结果的数量大于n的话,那么调用NamingEnumeration.hasMore()时,就会抛出一个SizeLimitExceededException。
// Set the search controls to limit the count to 1
SearchControls ctls = new SearchControls();
ctls.setCountLimit(1);
 
本教程中的样例代码中,为了便于阅读,都省略了try-catch子句,因为在教程中使用的代码都是片段,而在源代码中都包含了完整的try-catch子句。
javax.naming package中的异常如下:
javax.naming.directory package中的异常如下:
javax.naming.ldap package中的异常如下
InitialContext
在执行任何命名或者目录操作之前,需要获取一个initial context---到一个名称空间的切入点。这是由于所有对命名和目录服务的操作都是相对于上下文的。可以通过以下步骤获取一个initial context:
¨         选择对应服务的服务provider
¨         指定initial context需要的配置属性
¨         调用InitialContext类的构造器
设置服务provider。通过设置environment properties(Hashtable)来指定initial context使用的服务provider,并添加服务provider类的名字到环境属性。例如,如果使用Sun的LDAP服务provider,可以使用下面的代码来添加服务provider:
Hashtable env=new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,”com.sun.jndi.ldap.LdapCtxFactory”);
如果要指定Sun的文件系统服务provider,则为如下代码:
Hashtable env=new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,”com.sun.jndi.fxcontext.RefFSContextFactory”);
注:还可以通过system properties来指定服务provider。关于system properties和environment properties的详细介绍,在后面的教程中会涉及。
设置其他属性。不同的目录服务可能需要不同的信息。例如LDAP服务器需要指定LDAP服务器的链接地址,绑定的用户名和密码,如下所示:
env.put(Context.PROVIDER_URL,”ldap://localhost:389”);
env.put(Context.SECURITY_PRINCIPAL,”joeuser”);
env.put(Context.SECURITY_CREDENTIALS,”joepassword”);
而对于文件系统来说,需要的知识一个服务的URL:
env.put(Context.PROVIDER_URL,”file:/tmp/tutorial/”);
本教程中的LDAP服务器的地址为ldap://localhost:289/o=JNDITutorial,文件系统的路径为D:/workspace/JNDITutorial/tmp/tutorial/。
创建InitialContext。现在已经准备好创建initial context,将前面创建的env参数传递给InitialContext构造器即可。
Context ctx=new InitialContext(env);
当执行目录操作时,需要使用InitialDirContext,使用下面的构造器来构造:
DirContext ctx=new InitialDirContext(env);
 
Names
Context和DirContext接口都包含了重载的方法,一个接受java.lang.String参数,另外一个接受Name类的参数。当Name和java.lang.String参数只是简单的表示同一个名称时,每对冲在的函数是相同的。
 
附:Java扩展方法
The extension mechanism was introduced as a new feature in the JavaTM 1.2 platform. The extension mechanism provides a standard, scalable way to make custom APIs available to all applications running on the Java platform. As of the Java 1.3 platform release, Java extensions are also referred to as optional packages. This trail may use both terms interchangeably.
Extensions are groups of packages and classes that augment the Java platform through the extension mechanism. The extension mechanism enables the runtime environment to find and load extension classes without the extension classes having to be named on the class path. In that respect, extension classes are similar to the Java platform's core classes. That's also where extensions get their name -- they, in effect, extend the platform's core API.
Since this mechanism extends the platform's core API, its use should be judiciously applied. Most commonly it is used for well standarized interfaces such as those defined by the Java Community ProcessSM, although it may also be appropriate for site wide interfaces.
As the diagram indicates, extensions act as "add-on" modules to the Java platform. Their classes and public APIs are automatically available to any applications running on the platform.
The extension mechanism also provides a means for extension classes to be downloaded from remote locations for use by applets.
Extensions are bundled as Java Archive (JAR) files, and this trail assumes that you are familiar with the JAR file format. If you're not up to speed on JAR files, you might want to review some JAR-file documentation before proceeding with the lessons in this trail:
  • The Packaging Programs in JAR Files lesson in this tutorial.
  • The JAR Guide in the JDKTM documentation.
This trail has two lessons:
Creating and Using Extensions
This section shows you what you need to do to add an extension to your Java platform and how applets can benefit from the extension mechanism by downloading remote extension classes.
Making Extensions Secure
This section describes security privileges and permissions that are granted to extensions on your platform. You'll see how to use the Java platform's security architecture if you're writing extensions classes of your own.
Additional Documentation
You can find further information about extensions in the The Java Extensions Mechanism section of the JDK documentation. 
原创粉丝点击