《精通J2EE网络编程》中讲的JNDI 6.2 使用JNDI

本文介绍了Java Naming and Directory Interface (JNDI)的基础知识,包括使用JNDI进行对象绑定、查询、修改和删除等常见操作,并通过示例代码详细展示了如何在LDAP服务器中保存、查找和修改Java对象。
6.2 使用JNDI 

6.2.1  JNDI服务提供者

不使用服务提供者就不能用JNDI。一个服务提供者就是一组Java类的集合,它支持开发者同目录服务进行通信,其方式类似于JDBC驱动程序与数据库之间的通信方式。能够用于JNDI的服务提供者必须实现Context接口或Context的扩展接口Directory- Context。

在使用JNDI时,读者只需要了解JNDI,而服务提供者才关注实际的网络协议、编码/解码值等细节。

当下载SDK软件开发包时,同时就下载了Sun公司的一些现有的服务提供者。这些服务提供者包括LDAP、NIS、COS(CORBA对象服务)、RMI注册及文件系统的提供者。如:hashtableObj.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.ldapCtx- Fatory")就是表示使用Sun LDAP服务提供者。当然如果要使用IBM服务提供者时就可以用com.ibm.jndi.LDAPCtxFatory来代替com.sun.jndi.ldap.ldapCtxFatory。

6.2.2  JNDI的包

JNDI中包括5个包。

·       javax.naming:主要用于命名操作,它包含了命名服务的类和接口,该包定义了Context接口和InitialContext类;

·       javax.naming.directory:主要用于目录操作,它定义了DirContext接口和InitialDir- Context类;

·       javax.naming.event:在命名目录服务器中请求事件通知;

·       javax.naming.ldap:提供LDAP支持;

·       javax.naming.spi:允许动态插入不同实现,为不同命名目录服务供应商的开发人员提供开发和实现的途径,以便应用程序通过JNDI可以访问相关服务。

6.2.3  常用的JNDI操作

常用的JNDI操作如下:

·       void bind(String sName,Object object),绑定:把名称同对象关联的过程。

·       void rebind(String sName,Object object),重新绑定:用来把对象同一个已经存在的名称重新绑定。一般使用rebind()而不使用bind(),因为当有重名的时候rebind()不会出现异常,而bind()会报异常。

·       void unbind(String sName),释放:用来把对象从目录中释放出来。

·       void lookup(String sName,Object object),查找:返回目录总的一个对象。

·       void rename(String sOldName,String sNewName),重命名:用来修改对象名称绑定的名称。

·       NamingEnumeration listBindings(String sName),清单:返回绑定在特定上下文中指定属性名对象的清单列表,它返回名字、类和对象本身,它用于那些需要对对象进行实际操作的应用。具体使用如下:

java 代码

 

  1. //得到初始目录环境的一个引用   
  2.   
  3. Context cntxt = new InitialContext();   
  4.   
  5. //返回绑定在特定上下文中指定属性名对象的清单列表   
  6.   
  7. NamingEnumeration namEnumList = ctxt.listBinding("cntxtName");   
  8.   
  9. //循环列出所有名字、类和对象   
  10.   
  11. while ( namEnumList.hasMore() )  {   
  12.   
  13.     Binding bnd = (Binding) namEnumList.next();   
  14.   
  15.     String sObjName = bnd.getName();   
  16.   
  17.     String sClassName = bnd.getClassName();   
  18.   
  19.     //得到对象   
  20.   
  21.     SomeObject objLocal = (SomeObject) bnd.getObject();   
  22.   
  23. }   

·       NamingEnumeration list(String sName)与listBindings(String sName)相似,只是它只返回一系列名字/类映射,它主要是用于上下文浏览应用。

6.2.4  JNDI操作步骤

使用JNDI来访问命名服务或者目录服务,操作步骤如下:

(1)建立一个散列表(hashtable),它包含定义所希望使用的JNDI服务的属性,所希望连接的LDAP服务器IP地址以及工作的端口。

(2)将与认证成为用户登录有关的任何信息添加到散列表中。

(3)创建初始context对象。如果访问命名服务,则使用InitialContext类,如果访问目录服务,则要使用InitialDirContext类。

(4)使用刚才得到的context对象执行所需的操作(如添加新的条目或者搜索条目)。

(5)完成操作后关闭context对象。

6.2.5  JNDI允许存储的对象类型

JNDI最大的功能是能使用LDAP来存储需要在不同应用之间共享或者留做备用的对象。JNDI允许将下面几种与Java相关的对象类型存储到LDAP服务器内。

(1)串行化的Java对象。这是存储和取回已经串行化的Java对象的能力。也就是    说要存储的Java对象必须要实现Referenceable或Serializable接口类,否则该对象不能存储。

(2)标准的LDAP目录条目。它提供了操作标准目录数据的能力。标准目录数据的数据量比较小,可以在不同的语言之间共享它们。保持目录数据与编程语言的无关性对于要使用几种不同语言进行开发的大企业里是非常重要的。

(3)指向RMI Java对象的指针。RMI是用于分布式计算的,通过RMI,一个Java应用可以像本地一样调用一个远程类的方法。我们可以把一个可用的RMI类的引用存储在开发者的LDAP服务器中,而不必在每个装有RMI客户应用的计算机上都保持可用方法的注册。

6.2.6  JNDI存储查询串行化的Java对象

JNDI的主要目标是在网络上读/写Java对象。下面用具体实例来了解怎么使用JNDI。首先通过一个例子来讲解怎么样在LDAP中保存串行化的Java对象数据,再用一个例子来说明怎么对保存的对象数据进行查询、调用。

1.保存数据

在LDAP中保存数据就是在LDAP服务器中添加使用条目,也就是把条目绑定在服务器中。下面先建立一个基本类,再在另一个类中利用JNDI把这个基本类绑定在服务器中。

4 例6-1  在LDAP中保存数据。

(1)待绑定的基本类

java 代码
  1. package jndi;   
  2.   
  3. import java.io.serializable;   
  4.   
  5. public class persons  implements Serializable {   
  6.   
  7.     String Name = "";   
  8.   
  9.     String Age =""  ;   
  10.   
  11.     public persons () {   
  12.   
  13.     }   
  14.   
  15.     //构造函数,用于给变量赋值   
  16.   
  17.     public persons (String namePara,String age) {   
  18.   
  19.         Name = namePara;   
  20.   
  21.         Age = age;   
  22.   
  23.     }   
  24.   
  25.     //用于返回变量Name的值   
  26.   
  27.     public String getName() {   
  28.   
  29.         return Name;   
  30.   
  31.     }   
  32.   
  33.     //用于返回变量Age的值   
  34.   
  35.     public String getAge () {   
  36.   
  37.     return  Age;   
  38.   
  39.     }   
  40.   
  41. }   
  42.   

JNDI定义了一个Serializable接口类来为应用信息的表达提供一种统一的方式。Serializable接口类包含了诸如地址、类型信息等用于访问具体对象的信息。为了能将对象的引用绑定到目录树中,该对象的类必须实现Referenceable接口,其中包含了方法 getReference()。开发者可以在该对象上调用getReference()方法来获得Reference以用于绑定。Serializable接口与Referenceable接口有颇多相似之处,不同在于Referenceable可引用的对象只包含一些用于创建实际对象的信息,而Serializable会包含更多的甚至不适合存储在目录结构中的信息。

(2)绑定保存对象程序

java 代码
  1. package jndi;   
  2.   
  3. import java.util.Hashtable;   
  4.   
  5. import javax.naming.Context;   
  6.   
  7. import javax.naming.NamingException;       
  8.   
  9. import javax.naming.directory.*;   
  10.   
  11. public class ldapDataBind {   
  12.   
  13.    public static void main(String[]args){   
  14.   
  15.        //创建Hashtable以存储JNDI将用于连接目录服务的环境变量   
  16.   
  17.         Hashtable hs = new Hashtable();   
  18.   
  19.         //设置连接LDAP的实现工厂   
  20.   
  21.         hs.put(Context.INITIAL_CONTEXT_FACTORY,    
  22.   
  23.                       "com.sun.jndi.ldap.LdapCtxFactory");   
  24.   
  25.         // 指定LDAP服务器的主机名和端口号   
  26.   
  27.         hs.put(Context.PROVIDER_URL, "ldap://localhost:389 ");   
  28.   
  29.         //给环境提供认证方法,有SIMPLE、SSL/TLS和SASL   
  30.   
  31.         hs.put(Context.SECURITY_AUTHENTICATION, "simple");   
  32.   
  33.         //指定进入的目录识别名DN   
  34.   
  35.         hs.put(Context.SECURITY_PRINCIPAL, "cn=Directory Manager");   
  36.   
  37.         //进入的目录密码   
  38.   
  39.         hs.put(Context.SECURITY_CREDENTIALS, "password");   
  40.   
  41.         try {   
  42.   
  43.            // 得到初始目录环境的一个引用   
  44.   
  45.            DirContext ctx = new InitialDirContext(hs);   
  46.   
  47.            // 新建一个对象   
  48.   
  49.            persons perObj = new persons("jordan","40");   
  50.   
  51.            //绑定对象   
  52.   
  53.            ctx.rebind ("uid = Jordan,ou = Bull,o = NBA ",perObj);   
  54.   
  55.            System.out.println("bind object object success " );   
  56.   
  57.              /*实例化一个属性集合*/  
  58.   
  59.              Attributes  attrs =  new BasicAttributes(true);   
  60.   
  61.              /*建立一个属性,其属性名为"mail"*/  
  62.   
  63.             Attribute  personMail  = new BasicAttribute("mail");   
  64.   
  65.             //设置属性"mail"的值为"xie@163.com"、"liu@sina.com.cn"、   
  66.                  "xyh@powerise.com.cn"  
  67.   
  68.             personMail.add("xie@163.com");   
  69.   
  70.             personMail.add("liu@sina.com.cn");   
  71.   
  72.             personMail.add("xyh@powerise.com.cn");   
  73.   
  74.              attrs.put(personMail);   
  75.   
  76.              /*建立一个属性,其属性名为"uid",值为001*/  
  77.   
  78.             attrs.put("uid","001");   
  79.   
  80.             /*建立一个属性,其属性名为"cn",值为jordan1*/  
  81.   
  82.             attrs.put("cn","jordan1");   
  83.   
  84.             /*建立一个属性,其属性名为"sn",值为NBA */  
  85.   
  86.             attrs.put("sn","NBA");   
  87.   
  88.             /*建立一个属性,其属性名为"ou",值为bull */  
  89.   
  90.             attrs.put("ou","bull");   
  91.   
  92.             System.out.println("bind object object success " );   
  93.   
  94.             /* 在识别名为DN的目录中增加一个条目*/  
  95.   
  96.             ctx.createSubcontext("uid = Jordan, ou = Wizzard,o=NBA",attrs);   
  97.   
  98.            //关闭初始目录环境   
  99.   
  100.            ctx.close();   
  101.   
  102.         } catch (NamingException ex) {   
  103.   
  104.            System.err.println("bind object fail: " + ex.toString());   
  105.   
  106.         }     
  107.   
  108.    }   
  109.   
  110. }   
  111.   

2.使用JNDI查找数据

前面已经介绍了怎么样将对象数据绑定到服务器,现在开始介绍如何取得调用绑定在服务器上的对象数据。

5 例6-2  使用JNDI查找数据。

要调用对象数据,首先就必须用JNDI查找绑定的对象和数据,查找出来后,再调用该对象。程序如下所示。

java 代码
  1. package jndi;   
  2.   
  3. import java.nutil.Hashtable;   
  4.   
  5. import javax.naming.Context;   
  6.   
  7. import javax.naming.NamingException;   
  8.   
  9. import javax.naming.NamingEnumeration;   
  10.   
  11. import javax.naming.directory.*;   
  12.   
  13. public class findUseBindObj {   
  14.   
  15. public static void main(String[]args){   
  16.   
  17.          //创建Hashtable以存储JNDI将用于连接目录服务的环境变量   
  18.   
  19.        Hashtable hs = new Hashtable();   
  20.   
  21.         //设置连接Ldap的实现工厂   
  22.   
  23.         hs.put(Context.INITIAL_CONTEXT_FACTORY,   
  24.   
  25.                        "com.sun.jndi.ldap.LdapCtxFactory");   
  26.   
  27.         // 指定LDAP服务器IP地址为本机及端口号为389   
  28.   
  29.         hs.put(Context.PROVIDER_URL, "ldap://localhost:389");   
  30.   
  31.         try {   
  32.   
  33.            // 得到初始目录环境的一个引用   
  34.   
  35.            DirContext ctx = new InitialDirContext(hs);   
  36.   
  37.           //利用lookup查找返回指定DN的条目对象   
  38.   
  39.            persons pers =(persons)ctx.lookup("uid=Jordan,ou=Bull,o=NBA");   
  40.   
  41.            // 利用远程对象调用远程方法,返回Age变量的值   
  42.   
  43.            String  age    =  pers.getAge();   
  44.   
  45.            // 利用远程对象调用远程方法,返回Name变量的值   
  46.   
  47.            String  name  =  pers.getName();   
  48.   
  49.            //输出Name的值   
  50.   
  51.        System.out.println("name is :" +  name );   
  52.   
  53.        /*根据结点的DN来查找它的所有属性, 然后再从属性中得到所有的值,注意一个属性可  
  54.            以有多个值*/  
  55.   
  56.        Attributes attrs=ctx.getAttributes("uid=Jordan,ou=Wizzard,o=NBA");   
  57.   
  58.        //循环获取并输出这个属性的所有属性值   
  59.   
  60.        for(NamingEnumeration ae = attrs.getAll();ae.hasMore();){   
  61.   
  62.            //获取一个属性   
  63.   
  64.            Attribute attr = (Attribute)ae.next();   
  65.   
  66.            System.out.println("Attribute : " + attr.getID());   
  67.   
  68.                       //循环取得输出这个属性的所有属性值   
  69.   
  70.             for(NamingEnumeration ve = attr.getAll();ve.hasMore();){   
  71.   
  72.                         System.out.println("  Value : " + ve.next());   
  73.   
  74.         }   
  75.   
  76.         }   
  77.   
  78.         //成功打印提示信息   
  79.   
  80.         System.out.println("find object success " );   
  81.   
  82.         //调用该对象的函数   
  83.   
  84.        pers.toString();   
  85.   
  86.               
  87.   
  88.        //关闭初始目录环境   
  89.   
  90.        ctx.close();   
  91.   
  92.     } catch (NamingException ex) {   
  93.   
  94.        System.err.println(ex.toString());   
  95.   
  96.     }        
  97.   
  98.   }   
  99.   
  100. }   
  101.   

对于作为引用绑定在目录树中的对象,JNDI SPI 指定针对引用创建实际的对象。因此,在程序中只需要认为用lookup()方法返回的对象就是实际对象,而不用在调用什么方法来将引用转换为实际对象了,因为所有的工作都由JNDI内部完成了。

6.2.7  JNDI查询修改LDAP目录条目

前面已经介绍了如何在LDAP服务器里存储一个对象:主要是利用一个DN将对象绑定到LDAP服务器中,然后用lookup(DN)查找定位到绑定的对象,再对该对象进行操作。但是往往使用DN查找非常难,用户很难记住DN,因此我们可以使用其他属性(比如CN=Cherry)来检索包含那个属性的条目。下面来介绍JNDI中相关属性检索的具体使用。

1.修改条目

很多时候可能要对LDAP服务器上的条目进行修改,如修改用户密码,更新应用的配置等。但修改必须由一个已认证过的用户来执行,而且通常只能修改自己的密码而不能修改其他信息,管理助手能够修改电话号码和邮件地址,而修改用户标识这种工作由数据库管理员完成。

6 例6-3  用JNDI修改LDAP条目。

java 代码
  1. package jndi;   
  2.   
  3. import java.nutil.Hashtable;   
  4.   
  5. import javax.naming.Context;   
  6.   
  7. import javax.naming.directory.Attribute;   
  8.   
  9. import javax.naming.directory.Attributes;   
  10.   
  11. import javax.naming.directory.BasicAttribute;   
  12.   
  13. import javax.naming.directory.DirContext;   
  14.   
  15. import javax.naming.directory.InitialDirContext;   
  16.   
  17. import javax.naming.directory.ModificationItem;   
  18.   
  19. public class jndiPropertyModify {   
  20.   
  21.     public static void main(String[] args){   
  22.   
  23.         Hashtable hs = new Hashtable();   
  24.   
  25.         //设置连接LDAP的实现工厂为com.sun.jndi.ldap.LdapCtxFactory   
  26.   
  27.        hs.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.  
  28.             LdapCtxFactory");   
  29.   
  30.         //指定提供服务的服务器IP地址和端口号   
  31.   
  32.         hs.put(Context.PROVIDER_URL,"ldap://localhost:389");   
  33.   
  34.         //使用简单认证来认证用户   
  35.   
  36.         hs.put(Context.SECURITY_AUTHENTICATION,"simple");    
  37.   
  38.         hs.put(Context.SECURITY_PRINCIPAL,"uid=Jordan,ou=Bull,o=NBA");    
  39.   
  40.         hs.put(Context.SECURITY_CREDENTIALS,"good");   
  41.   
  42.         try {   
  43.   
  44.            /*指定了JNDI服务提供者中工厂类(factory class)的名称。Factory负  
  45.                责为其服务创建适当的InitialContext对象。在上面的代码片断中,为文件  
  46.                 系统服务提供者指定了工厂类*/  
  47.   
  48.            DirContext ctx = new InitialDirContext(hs);   
  49.   
  50.            System.out.println("成功创建初始化context对象!");   
  51.   
  52.               //新建生成一个修改条目类对象,用于存放条目属性   
  53.   
  54.            ModificationItem[] mdi = new ModificationItem[2];   
  55.   
  56.               // 把属性mail的值置为jordan@163.com   
  57.   
  58.            Attribute att0 = new BasicAttribute("mail",   
  59.                "jordan@163.com");   
  60.   
  61.               // 把属性call的值置为12745827   
  62.   
  63.            Attribute att1 = new BasicAttribute("call","12745827");   
  64.   
  65.              //修改指定属性mail   
  66.   
  67.            mdi[0]=new ModificationItem(DirContext.REPLACE_ATTRIBUTE,   
  68.                att0);    
  69.   
  70.             //增加新属性call到条目   
  71.   
  72.            mdi[1]=new ModificationItem(DirContext.ADD_ATTRIBUTE,att1);    
  73.   
  74.             // 修改指定DN条目的属性   
  75.   
  76.            ctx.modifyAttributes("uid=Jordan,ou=Bull,o=NBA",mdi);   
  77.   
  78.         }catch(Exception ex ){   
  79.   
  80.            ex.printStackTrace();   
  81.   
  82.            System.exit(1);   
  83.   
  84.         }   
  85.   
  86.     }   
  87.   
  88. }   
  89.   

上面程序的作用是修改前面例子中增加的DN为uid = Jordan,ou = Bull,o = NBA条目的属性。

在程序中用DirContext.REPLACE_ATTRIBUTE来修改条目的mail属性,在这里如果原来的mail属性有多个值时,都会被删掉,取而代之的是新赋的值。用DirContext. REPLACE_ATTRIBUTE时,如果原来的属性(mail)不存在时,就增加一个属性,有则修改。

用DirContext.ADD_ATTRIBUTE来将一个新属性增加到条目。真正起到修改作用的是ctx.modifyAttributes("uid = Jordan,ou = Bull,o = NBA",mdi)这条语句。

2.删除条目

有时,当开发者不需要某个条目时,就可以把它从LDAP服务器上删除。这只要通过调用参数为指定DN条目的DirContext接口的destorySubContext()方法来完成。

7 例6-4  用JNDI删除LDAP条目。

java 代码
  1. package jndi;   
  2.   
  3. import java.nutil.Hashtable;   
  4.   
  5. import javax.naming.Context;   
  6.   
  7. import javax.naming.directory.Attribute;   
  8.   
  9. import javax.naming.directory.Attributes;   
  10.   
  11. import javax.naming.directory.BasicAttribute;   
  12.   
  13. import javax.naming.directory.DirContext;   
  14.   
  15. import javax.naming.directory.InitialDirContext;   
  16.   
  17. import javax.naming.directory.ModificationItem;   
  18.   
  19. public class jndiPropertyModify {   
  20.   
  21.     public static void main(String[] args){   
  22.   
  23.         Hashtable hs = new Hashtable();   
  24.   
  25.         //设置连接LDAP的实现工厂为com.sun.jndi.ldap.LdapCtxFactory   
  26.   
  27.         hs.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.  
  28.             LdapCtxFactory");   
  29.   
  30.          //指定提供服务的服务器IP地址和端口号   
  31.   
  32.         hs.put(Context.PROVIDER_URL,"ldap://localhost:389");   
  33.   
  34.          //使用简单认证来认证用户   
  35.   
  36.         hs.put(Context.SECURITY_AUTHENTICATION,"simple");    
  37.   
  38.         // 指定DN   
  39.   
  40.         hs.put(Context.SECURITY_PRINCIPAL,"uid=Jordan,ou=Bull,o=NBA");    
  41.   
  42.         // 指定认证密码   
  43.   
  44.         hs.put(Context.SECURITY_CREDENTIALS,"good");   
  45.   
  46.         try {   
  47.   
  48.            /*指定了JNDI服务提供者中工厂类(factory class)的名称。Factory负  
  49.                责为其服务创建适当的InitialContext对象。在上面的代码片断中,为文件  
  50.                系统服务提供者指定了工厂类*/  
  51.   
  52.            DirContext ctx = new InitialDirContext(hs);   
  53.   
  54.              //删除指定DN条目   
  55.   
  56.            ctx.destroySubcontext("uid=Jordan,ou=Bull,o=NBA");   
  57.   
  58.         }catch(Exception ex ){   
  59.   
  60.            ex.printStackTrace();   
  61.   
  62.            System.exit(1);   
  63.   
  64.         }   
  65.   
  66.     }   
  67.   
  68. }   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值