jndi step by step(2)

(4) 目录服务操作

1、目录服务的操作

    我们会用LDAP作为例子来讲解目录服务的操作。与命名服务不同,目录服务的内容上下文的初始化方式需要
    改变:

java 代码
  1. // Set up the environment for creating the initial context   
  2. Hashtable env = new Hashtable();   
  3. env.put(Context.INITIAL_CONTEXT_FACTORY,    
  4.     "com.sun.jndi.ldap.LdapCtxFactory");   
  5. env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");   
  6. DirContext ctx = new InitialDirContext(env);  

 

    1.1、如何读取属性

    你可以使用 DirContext.getAttributes() 方法来读取一个对象的属性,你需要给这个方法传递一个对象的
    名字作为参数。例如一个对象在命名系统中的名字是“cn=Ted Geisel, ou=People”,那么我们可以使用如下
    的代码得到它的属性:

java 代码
  1. Attributes answer = ctx.getAttributes("cn=Ted Geisel, ou=People");  

    你也可以输出 “answer”来看看:

java 代码
  1. for (NamingEnumeration ae = answer.getAll(); ae.hasMore();) {   
  2.     Attribute attr = (Attribute)ae.next();   
  3.     System.out.println("attribute: " + attr.getID());   
  4.     /* Print each value */  
  5.     for (NamingEnumeration e = attr.getAll(); e.hasMore();   
  6.      System.out.println("value: " + e.next()))   
  7.     ;   
  8. }  

    输出结果是:

# java GetattrsAll
attribute: sn
value: Geisel
attribute: objectclass
value: top
value: person
value: organizationalPerson
value: inetOrgPerson
attribute: jpegphoto
value: [B@1dacd78b
attribute: mail
value: Ted.Geisel@JNDITutorial.com
attribute: facsimiletelephonenumber
value: +1 408 555 2329
attribute: telephonenumber
value: +1 408 555 5252
attribute: cn
value: Ted Geisel

    1.1.1、返回需要的属性

    有的时候我们只是需要得到一些属性,而不是全部的属性。这样,你可以把属性作为一个数组,把这个数组
    作为参数传递给那个方法。

java 代码
  1. // Specify the ids of the attributes to return   
  2. String[] attrIDs = {"sn""telephonenumber""golfhandicap""mail"};   
  3.   
  4. // Get the attributes requested   
  5. Attributes answer = ctx.getAttributes("cn=Ted Geisel, ou=People", attrIDs);  

    假设我们这个例子里的这个对象,拥有 "sn", "telephonenumber"和"mail"属性,但是没有“golfhandicap”
    属性,那么上面的代码返回的结果就应该是:

# java Getattrs
attribute: sn
value: Geisel
attribute: mail
value: Ted.Geisel@JNDITutorial.com
attribute: telephonenumber
value: +1 408 555 5252

    1.2、改变属性

    DirContext 接口有一些改变对象属性的方法。

    1.2.1、批量改变属性
 
    改变属性的方式之一就是批量改变属性,也就是使用许多 ModificationItem 对象来修改属性。
    每个 ModificationItem 对象都会有一个常量,来表示对属性进行什么样的操作。这些常量如下:

ADD_ATTRIBUTE
REPLACE_ATTRIBUTE
REMOVE_ATTRIBUTE

    对属性的改变会按照队列的顺序来执行,要么所有的改变都生效,要么都不生效。
    下面的代码演示了一个例子。它把“mail”这个属性的值,改变成了“geisel@wizards.com”,给“telephonenumber”
    属性增加了一个值,并且删除了“jpegphoto”属性。

java 代码
  1. // Specify the changes to make   
  2. ModificationItem[] mods = new ModificationItem[3];   
  3.   
  4. // Replace the "mail" attribute with a new value   
  5. mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,   
  6.     new BasicAttribute("mail""geisel@wizards.com"));   
  7.   
  8. // Add an additional value to "telephonenumber"   
  9. mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE,   
  10.     new BasicAttribute("telephonenumber""+1 555 555 5555"));   
  11.   
  12. // Remove the "jpegphoto" attribute   
  13. mods[2] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,   
  14.     new BasicAttribute("jpegphoto"));  

    上面的代码中,我们创建了一个修改属性的 ModificationItem 对象的列表(其实就是一个数组),然后执行
    modifyAttributes() 方法来修改属性。

java 代码
  1. // Perform the requested modifications on the named object   
  2. ctx.modifyAttributes(name, mods);  

 

    1.2.2 只修改某几个属性

    你可以不使用上面的方式,而对属性进行某一种操作:

java 代码
  1. // Save original attributes   
  2. Attributes orig = ctx.getAttributes(name,    
  3. new String[]{"mail""telephonenumber""jpegphoto"});   
  4.   
  5. 。。。。 。。。   
  6.   
  7. // Revert changes   
  8. ctx.modifyAttributes(name, DirContext.REPLACE_ATTRIBUTE, orig);  

 

    1.3、在目录服务中使用搜索功能

    1.3.1、基本的搜索功能
  
    最基本的搜索功能是可以指定一个对象的名字,和一些要搜索的属性的名字。
    下面的代码演示了这个功能。我们要进行这么一个搜索:对象必须有“sn”属性,而且数值必须是“Geisel”,
    而且必须有“mail”这个属性。

 

java 代码
  1. // Specify the attributes to match   
  2. // Ask for objects that has a surname ("sn") attribute with    
  3. // the value "Geisel" and the "mail" attribute   
  4. Attributes matchAttrs = new BasicAttributes(true); // ignore attribute name case   
  5. matchAttrs.put(new BasicAttribute("sn""Geisel"));   
  6. matchAttrs.put(new BasicAttribute("mail"));   
  7.   
  8. // Search for objects that have those matching attributes   
  9. NamingEnumeration answer = ctx.search("ou=People", matchAttrs);  

    你可以打印出这个结果:

java 代码
  1. while (answer.hasMore()) {   
  2.     SearchResult sr = (SearchResult)answer.next();   
  3.     System.out.println(">>>" + sr.getName());   
  4.     printAttrs(sr.getAttributes());   
  5. }  

 

    输出结果:

# java SearchRetAll
>>>cn=Ted Geisel
attribute: sn
value: Geisel
attribute: objectclass
value: top
value: person
value: organizationalPerson
value: inetOrgPerson
attribute: jpegphoto
value: [B@1dacd78b
attribute: mail
value: Ted.Geisel@JNDITutorial.com
attribute: facsimiletelephonenumber
value: +1 408 555 2329
attribute: cn
value: Ted Geisel
attribute: telephonenumber
value: +1 408 555 5252

    1.3.2、返回指定的属性

    上一个例子返回了满足条件的全部属性,当然你也可以只返回需要的属性,这仅仅需要把需要返回的属性,
    作为一个数组参数传递给 search() 方法。

java 代码
  1. // Specify the ids of the attributes to return   
  2. String[] attrIDs = {"sn""telephonenumber""golfhandicap""mail"};   
  3.   
  4. // Search for objects that have those matching attributes   
  5. NamingEnumeration answer = ctx.search("ou=People", matchAttrs, attrIDs);  

 

    1.4、搜索过滤

    在这里,你可以学到一个高级点的搜索方式,就是在搜索中使用过滤。在搜索中实现过滤,我们需要使用
    表达式来实现这个功能。下面的这个表达式就表示搜索条件是:对象必须有“sn”属性,而且数值必须是
    “Geisel”,而且必须有“mail”这个属性:

(&(sn=Geisel)(mail=*))

    下面的例子告诉你如何使用表达式来搜索:

java 代码
  1. // Create the default search controls   
  2. SearchControls ctls = new SearchControls();   
  3.   
  4. // Specify the search filter to match   
  5. // Ask for objects that have the attribute "sn" == "Geisel"   
  6. // and the "mail" attribute   
  7. String filter = "(&(sn=Geisel)(mail=*))";   
  8.   
  9. // Search for objects using the filter   
  10. NamingEnumeration answer = ctx.search("ou=People", filter, ctls);   

 

    下面这个列表有助你使用表达式:

       符号                   描述
        &       conjunction (i.e., and -- all in list must be true) 
        |       disjunction (i.e., or -- one or more alternatives must be true) 
        !       negation (i.e., not -- the item being negated must not be true) 
        =       equality (according to the matching rule of the attribute) 
        ~=      approximate equality (according to the matching rule of the attribute) 
        >=      greater than (according to the matching rule of the attribute) 
        <=      less than (according to the matching rule of the attribute) 
        =*      presence (i.e., the entry must have the attribute but its value is irrelevant) 
        *       wildcard (indicates zero or more characters can occur in that position);
                used when specifying attribute values to match 
        \       escape (for escaping '*', '(', or ')' when they occur inside an attribute value) 

    表达式中的每一个项目,都必须使用属性的名字,也可以使用属性的数值。例如“sn=Geisel”意味着属性
    “sn”的值为“Geisel”,而 "mail=*" 意味着属性 “mail” 必须存在,但是可以为任意值。
    每一个项目必须是在括号中,例如 "(sn=Geisel)"。不同的括号间使用逻辑判断符号来连接起来。
    例如 "(| (& (sn=Geisel) (mail=*)) (sn=L*))"。这表示 属性中 sn 必须等于Geisel 并且有 mail 这个
    属性 或者 有 sn 这个属性。
    详细的内容,请参考 http://www.ietf.org/rfc/rfc2254.txt


    当然了,你也可以只返回指定的属性,而不是全部的属性。

java 代码
        
  1. // Specify the ids of the attributes to return   
  2.     
  3. String[] attrIDs = {"sn""telephonenumber""golfhandicap""mail"};   
  4.     
  5. SearchControls ctls = new SearchControls();   
  6.     
  7. ctls.setReturningAttributes(attrIDs);  

 

    1.5 搜索控制
   
    在上面的章节中,我们已经看到了一个类:SearchControls,通过这个类,你可以控制搜索的行为。这里
    我们就来仔细地看看这个类。
 
    1.5.1 搜索范围

    SearchControls 类默认会在整个内容上下文(SearchControls.ONELEVEL_SCOPE)搜索对象,通过设置,你
    可以在某一个范围内搜索对象。

    1.5.1.1 在一个子树上搜索

    通过下面的代码你可以清晰地了解这一功能:

java 代码
        
  1. // Specify the ids of the attributes to return   
  2.     
  3. String[] attrIDs = {"sn""telephonenumber""golfhandicap""mail"};   
  4.     
  5. SearchControls ctls = new SearchControls();   
  6.     
  7. ctls.setReturningAttributes(attrIDs);   
  8.     
  9. ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);   
  10.     
  11.   
  12.     
  13. // Specify the search filter to match   
  14.     
  15. // Ask for objects that have the attribute "sn" == "Geisel"   
  16.     
  17. // and the "mail" attribute   
  18.     
  19. String filter = "(&(sn=Geisel)(mail=*))";   
  20.     
  21.   
  22.     
  23. // Search the subtree for objects by using the filter   
  24.     
  25. NamingEnumeration answer = ctx.search("", filter, ctls);  

 

    1.5.1.2 根据名字来搜索

    通过下面的代码你可以清晰地了解这一功能:

java 代码
        
  1. // Specify the ids of the attributes to return   
  2.     
  3. String[] attrIDs = {"sn""telephonenumber""golfhandicap""mail"};   
  4.     
  5. SearchControls ctls = new SearchControls();   
  6.     
  7. ctls.setReturningAttributes(attrIDs);   
  8.     
  9. ctls.setSearchScope(SearchControls.OBJECT_SCOPE);   
  10.     
  11.   
  12.     
  13. // Specify the search filter to match   
  14.     
  15. // Ask for objects that have the attribute "sn" == "Geisel"   
  16.     
  17. // and the "mail" attribute   
  18.     
  19. String filter = "(&(sn=Geisel)(mail=*))";   
  20.     
  21.   
  22.     
  23. // Search the subtree for objects by using the filter   
  24.     
  25. NamingEnumeration answer =    
  26.     
  27.     ctx.search("cn=Ted Geisel, ou=People", filter, ctls);  

 

    1.5.2 数量的限制

    通过下面的代码你可以控制返回结果的数量:

java 代码
        
  1. // Set the search controls to limit the count to 1   
  2.     
  3. SearchControls ctls = new SearchControls();   
  4.     
  5. ctls.setCountLimit(1);  

 

    1.5.3 时间的限制

    如果一个搜索耗费了很长的时间,那可不是一个好方法。这里你可以设置超时的时间。

java 代码
        
  1. // Set the search controls to limit the time to 1 second (1000 ms)   
  2.     
  3. SearchControls ctls = new SearchControls();   
  4.     
  5. ctls.setTimeLimit(1000);  

 

    参数的单位是毫秒。

    如果发生超时现象,那么就会抛出 TimeLimitExceededException。

    1.6 结合命名服务和目录服务的操作

    我们已经这样的一个概念,就是目录服务是命名服务的一个扩展。例如,之前我们说过命名服务具有 bind(),
    rebind(), createSubcontext() 等方法,但是在目录服务里却没有介绍这些方法。
    其实目录服务里也有这些方法。下面就用 LDAP 作为例子,介绍一下这些方法。

    1.6.1 创建一个具有属性的内容上下文

java 代码
        
  1. import javax.naming.*;   
  2.     
  3. import javax.naming.directory.*;   
  4.     
  5. import java.util.Hashtable;   
  6.     
  7.   
  8.     
  9. /**  
  10.     
  11.   * Demonstrates how to create a new subcontext called "ou=Fruits" with some   
  12.     
  13.   * attributes.  
  14.     
  15.   * (Run Destroy after this to remove the subcontext).  
  16.     
  17.   *  
  18.     
  19.   * usage: java Create  
  20.     
  21.   */  
  22.     
  23. class Create {   
  24.     
  25.     public static void main(String[] args) {   
  26.     
  27.   
  28.     
  29.     // Set up the environment for creating the initial context   
  30.     
  31.     Hashtable env = new Hashtable(11);   
  32.     
  33.     env.put(Context.INITIAL_CONTEXT_FACTORY,    
  34.     
  35.         "com.sun.jndi.ldap.LdapCtxFactory");   
  36.     
  37.     env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");   
  38.     
  39.   
  40.     
  41.     try {   
  42.     
  43.         // Create the initial context   
  44.     
  45.         DirContext ctx = new InitialDirContext(env);   
  46.     
  47.   
  48.     
  49.         // Create attributes to be associated with the new context   
  50.     
  51.         Attributes attrs = new BasicAttributes(true); // case-ignore   
  52.     
  53.         Attribute objclass = new BasicAttribute("objectclass");   
  54.     
  55.         objclass.add("top");   
  56.     
  57.         objclass.add("organizationalUnit");   
  58.     
  59.         attrs.put(objclass);   
  60.     
  61.   
  62.     
  63.         // Create the context   
  64.     
  65.         Context result = ctx.createSubcontext("ou=Fruits", attrs);   
  66.     
  67.   
  68.     
  69.         // Check that it was created by listing its parent   
  70.     
  71.         NamingEnumeration list = ctx.list("");   
  72.     
  73.   
  74.     
  75.         // Go through each item in list   
  76.     
  77.         while (list.hasMore()) {   
  78.     
  79.         NameClassPair nc = (NameClassPair)list.next();   
  80.     
  81.         System.out.println(nc);   
  82.     
  83.         }   
  84.     
  85.   
  86.     
  87.         // Close the contexts when we're done   
  88.     
  89.         result.close();   
  90.     
  91.         ctx.close();   
  92.     
  93.     } catch (NamingException e) {   
  94.     
  95.         System.out.println("Create failed: " + e);   
  96.     
  97.     }   
  98.     
  99.     }   
  100.     
  101. }  

    1.6.2 增加一个具有属性的绑定

java 代码
        
  1. import javax.naming.*;   
  2.     
  3. import javax.naming.directory.*;   
  4.     
  5.   
  6.     
  7. import java.util.Hashtable;   
  8.     
  9.   
  10.     
  11. /**  
  12.     
  13.   * Demonstrates how to add a binding and its attributes to a context.  
  14.     
  15.   * (Use Rebind example to overwrite binding; use Unbind to remove binding.)  
  16.     
  17.   *  
  18.     
  19.   * usage: java Bind  
  20.     
  21.   */  
  22.     
  23.   
  24.     
  25. class Bind {   
  26.     
  27.     public static void main(String[] args) {   
  28.     
  29.   
  30.     
  31.     // Set up the environment for creating the initial context   
  32.     
  33.     Hashtable env = new Hashtable(11);   
  34.     
  35.     env.put(Context.INITIAL_CONTEXT_FACTORY,    
  36.     
  37.         "com.sun.jndi.ldap.LdapCtxFactory");   
  38.     
  39.     env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");   
  40.     
  41.   
  42.     
  43.     try {   
  44.     
  45.         // Create the initial context   
  46.     
  47.         DirContext ctx = new InitialDirContext(env);   
  48.     
  49.   
  50.     
  51.         // Create object to be bound   
  52.     
  53.         Fruit fruit = new Fruit("orange");   
  54.     
  55.   
  56.     
  57.         // Create attributes to be associated with object   
  58.     
  59.         Attributes attrs = new BasicAttributes(true); // case-ignore   
  60.     
  61.         Attribute objclass = new BasicAttribute("objectclass");   
  62.     
  63.         objclass.add("top");   
  64.     
  65.         objclass.add("organizationalUnit");   
  66.     
  67.         attrs.put(objclass);   
  68.     
  69.   
  70.     
  71.         // Perform bind   
  72.     
  73.         ctx.bind("ou=favorite, ou=Fruits", fruit, attrs);   
  74.     
  75.   
  76.     
  77.         // Check that it is bound   
  78.     
  79.         Object obj = ctx.lookup("ou=favorite, ou=Fruits");   
  80.     
  81.         System.out.println(obj);   
  82.     
  83.   
  84.     
  85.         // Get its attributes   
  86.     
  87.         Attributes retattrs = ctx.getAttributes("ou=favorite, ou=Fruits");   
  88.     
  89.         GetattrsAll.printAttrs(retattrs);   
  90.     
  91.   
  92.     
  93.         // Close the context when we're done   
  94.     
  95.         ctx.close();   
  96.     
  97.     } catch (NamingException e) {   
  98.     
  99.         System.out.println("Operation failed: " + e);   
  100.     
  101.     }   
  102.     
  103.     }   
  104.     
  105. }  

    1.6.3 替换一个具有属性的绑定

java 代码

    

  • import javax.naming.*;   
  •     
  • import javax.naming.directory.*;   
  •     
  •   
  •     
  • import java.util.Hashtable;   
  •     
  •   
  •     
  • /**  
  •     
  •   * Demonstrates how to replace a binding and its attributes to a context.  
  •     
  •   * (Use after Bind example; use Unbind to remove binding.)  
  •     
  •   *  
  •     
  •   * usage: java Rebind  
  •     
  •   */  
  •     
  •   
  •     
  • class Rebind {   
  •     
  •     public static void main(String[] args) {   
  •     
  •   
  •     
  •     // Set up the environment for creating the initial context   
  •     
  •     Hashtable env = new Hashtable(11);   
  •     
  •     env.put(Context.INITIAL_CONTEXT_FACTORY,    
  •     
  •         "com.sun.jndi.ldap.LdapCtxFactory");   
  •     
  •     env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");   
  •     
  •   
  •     
  •     try {   
  •     
  •         // Create the initial context   
  •     
  •         DirContext ctx = new InitialDirContext(env);   
  •     
  •   
  •     
  •         // Create object to be bound   
  •     
  •         Fruit fruit = new Fruit("lemon");   
  •     
  •   
  •     
  •         // Create attributes to be associated with object   
  •     
  •         Attributes attrs = new BasicAttributes(true); // case-ignore   
  •     
  •         Attribute objclass = new BasicAttribute("objectclass");   
  •     
  •         objclass.add("top");   
  •     
  •         objclass.add("organizationalUnit");   
  •     
  •         attrs.put(objclass);   
  •     
  •   
  •     
  •         // Perform bind   
  •     
  •         ctx.rebind("ou=favorite, ou=Fruits", fruit, attrs);   
  •     
  •   
  •     
  •         // Check that it is bound   
  •     
  •         Object obj = ctx.lookup("ou=favorite, ou=Fruits");   
  •     
  •         System.out.println(obj);   
  •     
  •   
  •     
  •         // Get its attributes   
  •     
  •         Attributes retattrs = ctx.getAttributes("ou=favorite, ou=Fruits");   
  •     
  •         GetattrsAll.printAttrs(retattrs);   
  •     
  •   
  •     
  •         // Close the context when we're done   
  •     
  •         ctx.close();   
  •     
  •     } catch (NamingException e) {   
  •     
  •         System.out.println("Operation failed: " + e);   
  •     
  •     }   
  •     
  •     }   
  •     
  • (5) 高级应用之Name

    1、jndi 高级应用之 Name

        1.1、什么是 Name?
       
        这之前的文档中,我们所用的例子里,对象的名字都是 java.lang.String 类型的,也就是字符串类型。
        在这个文档里,我们则会介绍一些其他的对象名字类型,如 Name,以及他们的使用方法。
        我们首先会讲讲什么是字符串的名字和结构化的名字,以及它们的存在的必要性。
        然后,我们会介绍2个结构化名字的类:(复合名字)composite 和 (混合名字)compound。
        最后,我们介绍如何在对象名字中使用特殊字符,以及如何解析和构成一个复杂的对象的名字。
       
        1.2、字符串名字 vs 结构化名字
       
        在 Context 和 DirContext 接口里,每一个命名服务的方法都有2种方式,一个是接受字符串类型的名字,
        一个是接受结构化的名字(Name 类的对象)。例如:
       
    lookup(java.lang.String)
    lookup(javax.naming.Name)

        1.2.1、字符串名字
       
        使用字符串类型的名字,可以让你不必再生成一个CompositeName类的对象。例如下面的代码是相同的:

    java 代码
    1. Object obj1 = ctx.lookup("cn=Ted Geisel, ou=People, o=JNDITutorial");   
    2.   
    3. CompositeName cname = new CompositeName(   
    4.     "cn=Ted Geisel, ou=People, o=JNDITutorial");   
    5. Object obj2 = ctx.lookup(cname);  

     

        1.2.1、结构化的名字
       
        结构化的名字的对象可以是 CompositeName 或 CompoundName 类的对象,或者是任何一个实现了 “Name ”
        接口的类。
        如果是 CompositeName 类的实例,那么就被认为是一个复合的名字(composite name)。所谓的复合名字就
        是可以在一个名字里使用多个命名服务系统,而不是仅仅一个。
        如果是 compound 类的实例,那么就被认为是混合的名字(compound name)。混合的名字只包含一个命名服务
        系统。
       
        1.2.2、那么该使用哪种名字呢?
       
        一般来说,如果用户可以提供字符串类型的名字,那么就使用字符串类型的名字;可是如果用户提供的是一
        个组合的名字,那么就应该使用结构化的名字了。
        例如一个应用如果会涉及多个命名服务系统,那么就应该使用结构化的名字了。
       
        1.3 复合名字(composite name)
       
        复合名字就是跨越多个命名系统的名字。例如:
       
    cn=homedir,cn=Jon Ruiz,ou=People/tutorial/report.txt

        这个名字里包含了两个命名系统:一个 LDAP 系统 "cn=homedir,cn=Jon Ruiz,ou=People" 和一个文件系统
        "tutorial/report.txt"。当你把这个字符串传递给 Context 的 look 方法,那么就会从 LDAP 系统里查找
        那个文件,然后返回那个对象(一个文件对象的实例)。当然,这取决于特定的SPI。例如:

    java 代码
    1. File f = (File)ctx.lookup(   
    2.     "cn=homedir,cn=Jon Ruiz,ou=People/tutorial/report.txt");   

     

        1.3.1、字符串的表现形式
       
        我们可以这样想像,一个复合的名字是由不同的“组件”组成的。所谓的“组件”,我们可以想象成是一个名字
        的一小部分,每一个组件表示一个命名服务系统。每一个组件都由正斜杠“/”分开。
        例如 :cn=homedir,cn=Jon Ruiz,ou=People/tutorial/report.txt
        包含三个组件:
       
    cn=homedir,cn=Jon Ruiz,ou=People
    tutorial
    report.txt

        第一个组件属于LDAP命名服务系统,第2和第3个组件属于文件命名服务系统。正如我们从这个例子里看到的,
        多个组件(tutorial 和 report.txt)可以属于同一个命名服务系统,但是一个组件是不能跨越多个命名服务
        系统的。
       
        除了使用正斜杠“/”外,复合名字还允许使用其它三个特殊符号:反斜杠“\",单引号"'",双引号" " "。
        这三个符号是内置的符号,也就是说他们三个具有特殊的用途。
       
        反斜杠“\”的作用是转译字符。反斜杠“\”之后的字符,会被认为是普通字符。
        例如“a\/b”,其中的“/”是不会被认为是不同的命名服务系统的分隔符号。
       
        双引号和单引号可以让引号内的字符成为普通字符,例如下面的用法都是一样的:
       
    a\/b\/c\/d
    "a/b/c/d"
    'a/b/b/d'

        可见,有的时候使用引号还是很方便的。
       
        复合的名字也可以是空的,空的复合名字意味着没有组件,一般用空的字符串表示。
       
        复合名字的组件也可以是空的,例如:
       
    /abc
    abc/
    abc//xyz

        1.3.2、复合名字的类
       
        CompositeName 类是用来构成复合名字的类。你可以给它的构造函数传递一个复合名字的字符串。
        例如:

    java 代码
    1. import javax.naming.CompositeName;   
    2. import javax.naming.InvalidNameException;   
    3.   
    4. /**  
    5.   * Demonstrates how to construct a composite name given its  
    6.   * string representation  
    7.   *  
    8.   * usage: java ConstructComposite   
    9.   */  
    10. class ConstructComposite {   
    11.     public static void main(String[] args) {   
    12.     if (args.length != 1) {   
    13.         System.out.println("usage: java ConstructComposite <string></string>");   
    14.         System.exit(-1);   
    15.     }   
    16.     String name = args[0];   
    17.     try {   
    18.         CompositeName cn = new CompositeName(name);   
    19.         System.out.println(cn + " has " + cn.size() + " components: ");   
    20.         for (int i = 0; i < cn.size(); i++) {   
    21.         System.out.println(cn.get(i));   
    22.         }   
    23.     } catch (InvalidNameException e) {   
    24.         System.out.println("Cannot parse name: " + name);   
    25.     }   
    26.     }   
    27. }   

     

        运行这个例子,输出的结果就是:
       
    a/b/c has 3 components:
    a
    b
    c

        CompositeName 类有许多方法,例如查看,修改,比较,以及得到一个复合名字的字符串表现形式。
       
        1.3.3、访问复合名字里的组件
       
        可以通过下列方法访问复和名字里的组件:
       
    get(int posn)
    getAll()
    getPrefix(int posn)
    getSuffix(int posn)
    clone()

        如果你想得到特定位置的组件的名字,那么使用 get(int posn) 方法就非常合适。
       
        getAll() 方法可以返回全部的组件的名字。
        例如:

    java 代码
    1. try {   
    2.     CompositeName cn = new CompositeName(name);   
    3.     System.out.println(cn + " has " + cn.size() + " components: ");   
    4.     for (Enumeration all = cn.getAll(); all.hasMoreElements();) {   
    5.     System.out.println(all.nextElement());   
    6.     }   
    7. catch (InvalidNameException e) {   
    8.     System.out.println("Cannot parse name: " + name);   
    9. }  

        你可以用 getPrefix(int posn) 和 getSuffix(int posn)来从前端或者后端查找组件的名字,如:

    java 代码
    1. CompositeName cn = new CompositeName("one/two/three");   
    2. Name suffix = cn.getSuffix(1);  // 1 <= index < cn.size()   
    3. Name prefix = cn.getPrefix(1);  // 0 <= index < 1  

     

        运行结果:
       
    two/three
    one

        1.3.4、修改一个复合名字
       
        你可以通过下列方法修改一个复合名字:

    add(String comp)
    add(int posn, String comp)
    addAll(Name comps)
    addAll(Name suffix)
    addAll(int posn, Name suffix)
    remove(int posn)

        当你创建了一个复合名字实例后,你可以对它进行修改。看看下面的例子:

    java 代码
    1. CompositeName cn = new CompositeName("1/2/3");   
    2. CompositeName cn2 = new CompositeName("4/5/6");   
    3. System.out.println(cn.addAll(cn2));           // 1/2/3/4/5/6   
    4. System.out.println(cn.add(0"abc"));         // abc/1/2/3/4/5/6   
    5. System.out.println(cn.add("xyz"));            // abc/1/2/3/4/5/6/xyz   
    6. System.out.println(cn.remove(1));             // 1   
    7. System.out.println(cn);               // abc/2/3/4/5/6/xyz   

     

        1.3.4、比较复合名字
       
        你可以通过以下的方法对复合名字进行比较:
       
    compareTo(Object name)
    equals(Object name)
    endsWith(Name name)
    startsWith(Name name)
    isEmpty()

        你可以使用 compareTo(Object name) 方法对一个复合名字的列表进行排序。下面是一个例子:

    java 代码
    1. import javax.naming.CompositeName;   
    2. import javax.naming.InvalidNameException;   
    3.   
    4. /**  
    5.   * Demonstrates how to sort a list of composite names.  
    6.   *  
    7.   * usage: java SortComposites [<name></name>]*  
    8.   */  
    9. class SortComposites {   
    10.     public static void main(String[] args) {   
    11.     if (args.length == 0) {   
    12.         System.out.println("usage: java SortComposites [<names></names>]*");   
    13.         System.exit(-1);   
    14.     }   
    15.     CompositeName[] names = new CompositeName[args.length];   
    16.     try {   
    17.         for (int i = 0; i < names.length; i++) {   
    18.         names[i] = new CompositeName(args[i]);   
    19.         }   
    20.   
    21.         sort(names);   
    22.   
    23.         for (int i = 0; i < names.length; i++) {   
    24.         System.out.println(names[i]);   
    25.         }   
    26.     } catch (InvalidNameException e) {   
    27.         System.out.println(e);   
    28.     }   
    29.     }   
    30.   
    31.     /**  
    32.      * Use bubble sort.  
    33.      */  
    34.     private static void sort(CompositeName[] names) {   
    35.     int bound = names.length-1;   
    36.     CompositeName tmp;   
    37.   
    38.     while (true) {   
    39.         int t = -1;   
    40.         for (int j=0; j < bound; j++) {   
    41.         int c = names[j].compareTo(names[j+1]);   
    42.         if (c > 0) {   
    43.             tmp = names[j];   
    44.             names[j] = names[j+1];   
    45.             names[j+1] = tmp;   
    46.             t = j;   
    47.         }   
    48.         }   
    49.         if (t == -1break;   
    50.         bound = t;   
    51.     }   
    52.     }   
    53. }  

        equals() 方法可以让你比较两个复合名字是否相同。只有两个复合名字有相同的组件,而且顺序一样,
        会返回 true。
       
        使用 startsWith() 和 endsWith()方法,你可以判断复合名字是以什么字符串开头和以什么字符串结尾。
       
        isEmpty() 方法可以让你知道一个复合名字是否为空。你也可以使用 size() == 0 来实现同样的功能。
       
        下面是一些例子:

    java 代码
    1. CompositeName one = new CompositeName("cn=fs/o=JNDITutorial/tmp/a/b/c");   
    2. CompositeName two = new CompositeName("tmp/a/b/c");   
    3. CompositeName three = new CompositeName("cn=fs/o=JNDITutorial");   
    4. CompositeName four = new CompositeName();   
    5.   
    6. System.out.println(one.equals(two));        // false   
    7. System.out.println(one.startsWith(three));  // true   
    8. System.out.println(one.endsWith(two));      // true   
    9. System.out.println(one.startsWith(four));   // true   
    10. System.out.println(one.endsWith(four));     // true   
    11. System.out.println(one.endsWith(three));    // false   
    12. System.out.println(one.isEmpty());      // false   
    13. System.out.println(four.isEmpty());     // true   
    14. System.out.println(four.size() == 0);       // true  

        1.3.5、复合名字的字符串表现形式
       
        你可以使用 toString() 方法来实现这个功能。
        下面是一个例子:

    java 代码
    1. import javax.naming.CompositeName;   
    2. import javax.naming.InvalidNameException;   
    3.   
    4. /**  
    5.   * Demonstrates how to get the string representation of a composite name.  
    6.   *  
    7.   * usage: java CompositeToString   
    8.   */  
    9. class CompositeToString {   
    10.     public static void main(String[] args) {   
    11.     if (args.length != 1) {   
    12.         System.out.println("usage: java CompositeToString <string></string>");   
    13.         System.exit(-1);   
    14.     }   
    15.     String name = args[0];   
    16.     try {   
    17.         CompositeName cn = new CompositeName(name);   
    18.         String str = cn.toString();   
    19.         System.out.println(str);   
    20.         CompositeName cn2 = new CompositeName(str);   
    21.         System.out.println(cn.equals(cn2));     // true   
    22.     } catch (InvalidNameException e) {   
    23.         System.out.println("Cannot parse name: " + name);   
    24.     }   
    25.     }   
    26. }  

        1.3.5、复合名字作为Context的参数
       
        直接看一个例子,非常简单:

    java 代码
    1. // Create the initial context   
    2. Context ctx = new InitialContext(env);   
    3.        
    4. // Parse the string name into a CompositeName   
    5. Name cname = new CompositeName(   
    6.     "cn=homedir,cn=Jon Ruiz,ou=people/tutorial/report.txt");   
    7.   
    8. // Perform the lookup using the CompositeName   
    9. File f = (File) ctx.lookup(cname);  

     

        1.4、混合名字
       
        混合名字不是跨越命名系统的,例如:
       
        cn=homedir,cn=Jon Ruiz,ou=People
       
        它和复合名字有些类似,我们也可以把它看成是由不同的组件组成的,这个名字里包含了三个组件:
       
        ou=People
        cn=Jon Ruiz
        cn=homedir
       
        1.4.1、混合名字和复合名字的关系
       
        当你给 Context.lookup() 传递一个字符串的时候,首先 lookup 方法会把这个字符串作为复合名字来看
        待,这个复合名字可能只包含一个组件。但是一个组件可能会包含几个混合名字。
       
        1.4.2、混合名字的字符串表现方式
       
        正如上面所说的,一个混合名字是由很多的组件组成的。组件之间的分隔符号依赖于特定的命名服务系统。
        例如在 LDAP 里,分隔符好就是“,”,因此,下面的这个混合名字
       
    ou=People
    cn=Jon Ruiz
    cn=homedir

        的字符串形式就是 cn=homedir,cn=Jon Ruiz,ou=People

        1.4.3、混合名字的类
       
        处理混合名字,我们可以使用 CompoundName 类。你可以向它的构造函数传递混合名字的字符串,并且还
        得设置一些必要的属性,这些属性一般都是特定的命名服务系统的一些规则。
       
        实际上,只有当你准备编写一个SPI的时候,才会去使用装个构造函数。作为一个开发者,一般你只是会
        涉及混合名字里的各个组件而已。
        下面是一个例子:

    java 代码
    1. import javax.naming.*;   
    2. import java.util.Hashtable;   
    3.   
    4. /**  
    5.   * Demonstrates how to get a name parser and parse a name.  
    6.   *  
    7.   * usage: java ParseCompound  
    8.   */  
    9. class ParseCompound {   
    10.     public static void main(String[] args) {   
    11.     // Set up environment for creating initial context   
    12.     Hashtable env = new Hashtable(11);   
    13.     env.put(Context.INITIAL_CONTEXT_FACTORY,    
    14.         "com.sun.jndi.ldap.LdapCtxFactory");   
    15.     env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");   
    16.   
    17.     try {   
    18.         // Create the initial context   
    19.         Context ctx = new InitialContext(env);   
    20.   
    21.         // Get the parser for this namespace   
    22.         NameParser parser = ctx.getNameParser("");   
    23.   
    24.         // Parse name   
    25.         Name compoundName = parser.parse("cn=John,ou=People,ou=Marketing");   
    26.   
    27.         // List components in name   
    28.         for (int i = 0; i < compoundName.size(); i++) {   
    29.         System.out.println(compoundName.get(i));   
    30.         }   
    31.   
    32.         // Close ctx when done   
    33.         ctx.close();   
    34.     } catch (NamingException e) {   
    35.         System.out.println(e);   
    36.     }   
    37.     }   
    38. }   

        1.4.4、操作混合名字类
       
        注意,上面的例子中,我们使用 NameParser.parse() 来返回一个实现了Name接口的对象。这个接口可以
        是  CompositeName类 或 CompoundName类。这就意味着,你可以访问或者修改一个混合名字对象,就好
        像我们在复合名字里做的一样!
        下面是一个例子:

    java 代码
    1. // Get the parser for this namespace   
    2. NameParser parser = ctx.getNameParser("");   
    3.   
    4. // Parse the name   
    5. Name cn = parser.parse("cn=John,ou=People,ou=Marketing");   
    6.   
    7. // Remove the second component from the head   
    8. System.out.println(cn.remove(1));          // ou=People   
    9.   
    10. // Add to the head (first)   
    11. System.out.println(cn.add(0"ou=East"));  // cn=John,ou=Marketing,ou=East   
    12.   
    13. // Add to the tail (last)   
    14. System.out.println(cn.add("cn=HomeDir"));  // cn=HomeDir,cn=John,ou=Marketing,ou=East  

     

        输出结果:
       
    ou=People
    cn=John,ou=Marketing,ou=East
    cn=HomeDir,cn=John,ou=Marketing,ou=East

        需要的注意的是,LDAP系统里,组件的顺序是从右到左的。也就是右边是名字的开头,而左边是名字
        的结尾!
       
        下面这个例子是修改混合名字的:

    java 代码
    1. import javax.naming.*;   
    2. import java.util.Hashtable;   
    3. import java.io.File;   
    4.   
    5. /**  
    6.   * Demonstrates how to modify a compound name by adding and removing components.  
    7.   * Uses file system syntax.  
    8.   *  
    9.   * usage: java ModifyCompoundFile  
    10.   */  
    11. class ModifyCompoundFile {   
    12.     public static void main(String[] args) {   
    13.     // Set up environment for creating initial context   
    14.     Hashtable env = new Hashtable(11);   
    15.     env.put(Context.INITIAL_CONTEXT_FACTORY,    
    16.         "com.sun.jndi.fscontext.RefFSContextFactory");   
    17.   
    18.     try {   
    19.         // Create the initial context   
    20.         Context ctx = new InitialContext(env);   
    21.   
    22.         // Get the parser for this namespace   
    23.         NameParser parser = ctx.getNameParser("");   
    24.   
    25.         // Parse name   
    26.         Name cn = parser.parse("Marketing" + File.separator +    
    27.         "People" + File.separator +   
    28.         "John");   
    29.   
    30.         // Remove 2nd component from head   
    31.         System.out.println(cn.remove(1));             // People   
    32.   
    33.         // Add to head (first)   
    34.         System.out.println(cn.add(0"East"));     
    35.                         // East/Marketing/John   
    36.   
    37.         // Add to tail (last)   
    38.         System.out.println(cn.add("HomeDir"));   
    39.                         // /East/Marketing/John/HomeDir   
    40.   
    41.         // Close ctx when done   
    42.         ctx.close();   
    43.     } catch (NamingException e) {   
    44.         System.out.println(e);   
    45.     }   
    46.     }   
    47. }  

     

        这个例子使用了文件系统,而不是LDAP系统,输出结果:
       
    People
    East/Marketing/John
    East/Marketing/John/HomeDir

        1.4.5、混合名字作为Context的参数
       
        我们只用一个例子就可以了:

    java 代码
    1. // Create the initial context   
    2. Context ctx = new InitialContext(env);   
    3.   
    4. // Get the parser for the namespace   
    5. NameParser parser = ctx.getNameParser("");   
    6.   
    7. // Parse the string name into a compound name   
    8. Name compound = parser.parse("cn=Jon Ruiz,ou=people");   
    9.   
    10. // Perform the lookup using the compound name   
    11. Object obj = ctx.lookup(compound);  

        1.4.6、取得完整的混合名字
       
        有的时候,你可能需要根据一个混合名字来的完整的名字。例如一个DNS,你只知道一部分如“www.abc.com”,
        但实际上它的完整的DNS是“www.abc.com.cn”。
       
        其实这个功能已经超出了jndi 的 api 所控制的范围了,因为 jndi 是不能决定完整的名字到底是什么样子,
        那必须依赖特定的命名服务系统。
       
        但是,jndi 仍然提供了一个接口 Context.getNameInNamespace()。这个方法的返回结果,依赖于你所使用
        的命名服务系统。
       
        下面是一个例子:
       

    java 代码
    1. // Create the initial context   
    2. Context ctx = new InitialContext(env);   
    3.   
    4. // Perform the lookup    
    5. Context jon = (Context)ctx.lookup("cn=Jon Ruiz,ou=people");   
    6.   
    7. String fullname = jon.getNameInNamespace();  

     

        输出结果:
       
        cn=Jon Ruiz,ou=people,o=JNDItutorial

       
        1.5、名字解析
       
        名字解析的意思就是根据一个名字的字符串形式,把它解析成结构化的形式,如混合名字或复合名字。
        jndi API里包含了这样的接口,其接口的实现,依赖于特定的SPI。
       
        1.5.1、解析复合名字
       
        下面是一个例子:

    java 代码
    1. // Parse the string name into a CompositeName   
    2. Name cname = new CompositeName(   
    3.     "cn=homedir,cn=Jon Ruiz,ou=people/tutorial/report.txt");  

        其实这个用法我们早就在前面看过了。
       
        1.5.2、解析混合名字
       
        为了解析混合名字,你必须使用 NameParser 接口,这个接口有一个方法:

    java 代码
    1. Name parse(String name) throws InvalidNameException  

        首先,你必须从 SPI 里得到一个 NameParser 接口的实现:

    java 代码
    1. // Create the initial context   
    2. Context ctx = new InitialContext();   
    3.   
    4. // Get the parser for LDAP   
    5. NameParser ldapParser =    
    6.     ctx.getNameParser("ldap://localhost:389/o=jnditutorial");   
    7.   
    8. // Get the parser for filenames   
    9. NameParser fsParser = ctx.getNameParser("file:/");  

        一旦你得到了 NameParser 实例,你就可以用它来把字符串形式的名字转换成结构化的名字。

    java 代码
    1. // Parse the name using the LDAP parser   
    2. Name compoundName = ldapParser.parse("cn=John Smith, ou=People, o=JNDITutorial");   
    3.   
    4. // Parse the name using the LDAP parser   
    5. Name compoundName = fsParser.parse("tmp/tutorial/beyond/names/parse.html");  

        如果解析错误,那么会得到 InvalidNameException 。
       
        尽管 parse() 方法可以返回一个结构化的名字,但是我们还是建议仅仅用它来处理混合名字,而不要
        处理复合名字。
        parse()返回的对象未必一定是 CompoundName 类,只要返回的对象实现了 Name 接口就可以--这依赖
        于特定的SPI。
       
       
        1.6、动态生成复合名字
       
        之前我们介绍了访问,操作复合名字的方法。但是还有一些难以处理的情况需要我们去面对。
        例如,如果你要给一个复合名字增加一个组件的话,你是增加复合名字的组件还是混合名字的组件?
        如果是混合名字的组件,那么使用什么规则呢?
        例如我们有这样一个复合名字:
       
        cn=homedir,cn=Jon Ruiz/tutorial
       
        现在你需要增加一个文件名字,如果是windows系统,那么就是:
       
        cn=homedir,cn=Jon Ruiz/tutorial\report.txt
       
        然后我们再增加一个LDAP混合名字 ou=People,那么就需要使用LDAP的规则:
       
        cn=homedir,cn=Jon Ruiz,ou=People/tutorial\report.txt
       
        在这个例子中,我们使用了不同的命名服务系统,我们需要知道什么时候因该使用什么样的命名系统
        的规则,这真的很麻烦。
       
        为了解决这个问题,jndi API 提供了一个接口 Context.composeName(),它用来动态的组装名字,
        当然依赖于特定的SPI了。
        你需要给它提供两个参数:一个是需要追加的组件,一个是被追加的组件的名字。
       
        下面是一个例子:

    1. // Create the initial context  
    2. Context ctx = new InitialContext(env);  
    3.   
    4. // Compose a name within the LDAP namespace  
    5. Context ldapCtx = (Context)ctx.lookup("cn=Jon Ruiz,ou=people");  
    6. String ldapName = ldapCtx.composeName("cn=homedir""cn=Jon Ruiz,ou=people");  
    7. System.out.println(ldapName);  
    8.   
    9. // Compose a name when it crosses into the next naming system  
    10. Context homedirCtx = (Context)ctx.lookup(ldapName);  
    11. String compositeName = homedirCtx.composeName("tutorial", ldapName);  
    12. System.out.println(compositeName);  
    13.   
    14. // Compose a name within the File namespace  
    15. Context fileCtx = (Context)ctx.lookup(compositeName);  
    16. String fileName = fileCtx.composeName("report.txt", compositeName);  
    17. System.out.println(fileName); 

    (6) 高级应用之 环境属性

    1、环境属性

        在之前的文档里,我们已经学会如何去初始化一个内容上下文,例如:

    java 代码
    1. Hashtable env = new Hashtable();   
    2. env.put(Context.INITIAL_CONTEXT_FACTORY,    
    3.     "com.sun.jndi.ldap.LdapCtxFactory");   
    4. env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");   
    5.   
    6. Context ctx = new InitialContext(env);  

     

        这个例子中的 Hashtable,就是一个环境属性,或者简单的说是“环境”。
        这个章节里,我们要介绍的就是这个“环境属性”,看看他们是如何发挥作用的,以及如何使用的等等。
       
        JNDI其实只是一个接口,为了访问一个命名/目录服务,你必须依赖特定的SPI。这就需要进行一些配置,
        你得告诉JNDI,你需要的SPI是什么。
       
        下面是一些不同种类的环境属性:
       
        a> 标准的
        b> 描述服务的
        c> 描述特性的
        d> 描述提供者的。
       
        下面是标准的环境属性:

       

     

        1.1、环境属性
       
        配置环境属性可以通过两种方式:一个是把一个Hashtable传递给InitialContext的构造函数,另一个
        是用一个 .properties 文件。
        一些JNDI的环境属性还可以通过系统变量或者Applet参数来设置。
       
        1.1.1、应用程序资源文件(.properties文件)
       
        你可以在 .properties文件 里指定JNDI的配置。这个文件的名字应该是 jndi.properties。例如下面
        就是一个 jndi.properties的例子:
       
    java.naming.factory.object=com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person
    java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person
    java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory
    java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
    java.naming.provider.url=ldap://localhost:389/o=jnditutorial
    com.sun.jndi.ldap.netscape.schemaBugs=true

        首先,应用程序会从classpath 里面载入 jndi.properties,如果没有发现,那么会载入JAVA_HOME/lib/
        jndi.properties。
       
        注意:如果你使用这种方式配置JNDI,那么当找不到装个文件的时候,会抛出异常。
       
        当然,我们初始化内容上下文的用法也需要修改一下:

    java 代码
    1. InitialContext ctx = new InitialContext();  

     

        1.1.2、通过系统变量设置JNDI
       
        我们可以使用如下的方式来实现这个功能:
       
        # java -Djava.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory \
          -Djava.naming.provider.url=ldap://localhost:389/o=jnditutorial \
          List

        1.1.3、Applet的方式设置JDNI
       
        看这个例子:

    java 代码
    1. name=java.naming.factory.initial   
    2. value=com.sun.jndi.ldap.LdapCtxFactory>   
    3.   
    4.     
    5. name=java.naming.provider.url   
    6. value=ldap://localhost:389/o=jnditutorial>  

        同时初始内容上下文也需要修改:

    java 代码
    1. // Put this applet instance into the environment   
    2. Hashtable env = new Hashtable();   
    3. env.put(Context.APPLET, this);   
    4.   
    5. // Pass the environment to the initial context constructor   
    6. Context ctx = new InitialContext(env);   
    7.   
    8. // List the objects    
    9. NamingEnumeration enum = ctx.list(target);   
    10. while (enum.hasMore()) {   
    11.      out.println(enum.next());   
    12. }   
    13. ctx.close();  

        1.2、内容上下文环境的探讨
       
        我们已经知道可以通过三种方式来设置JDNI的属性。但是,如果我们同时使用了两种方式会
        怎么样呢?看看下面的例子:

    java 代码
    1. // Initial environment with various properties   
    2. Hashtable env = new Hashtable();   
    3. env.put(Context.INITIAL_CONTEXT_FACTORY,     
    4.     "com.sun.jndi.fscontext.FSContextFactory");   
    5. env.put(Context.PROVIDER_URL, "file:/");   
    6. env.put(Context.OBJECT_FACTORIES, "foo.bar.ObjFactory");   
    7. env.put("foo""bar");   
    8.   
    9. // Call the constructor   
    10. Context ctx = new InitialContext(env);   
    11.   
    12. // See what environment properties you have   
    13. System.out.println(ctx.getEnvironment());  

     

        我们同时配置一个jndi.properties在classpath里:
       
    java.naming.factory.object=com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person
    java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person
    java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory
    java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
    java.naming.provider.url=ldap://localhost:389/o=jnditutorial
    com.sun.jndi.ldap.netscape.schemaBugs=true

        然后看看运行的结果:
       
    com.sun.jndi.ldap.netscape.schemaBugs=true
    java.naming.factory.object=foo.bar.ObjFactory:com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person
    java.naming.factory.initial=com.sun.jndi.fscontext.FSContextFactory
    foo=bar
    java.naming.provider.url=file:/
    java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person
    java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory

        下面来分析一下这个结果:
       
        a> 在 Hashtable 里的“foo”项,和在文件里的 “com.sun.jndi.ldap.netscape.schemaBugs”,
           都出现在结果里。
        b> “java.naming.factory.object” 项是两者的组合。
        c> 其他的属性如“java.naming.factory.initial” 都是用的 Hashtable里的。
       
        是不是有些混乱?如果我们要使用多个SPI该怎么办?没关系,我们在下一个小结里介绍这个问题。
       
        1.3、定制使用SPI
       
        你可以通过一个SPI的属性文件,来为某一个SPI进行单独的设置。这个文件也是 .properties 文件。
        SPI的属性文件应该类似下面:
       
        前缀/jndiprovider.properties
       
        这个前缀是什么呢?就是这个SPI实现的内容上下文类(Context)的包的结构。
        例如,我们要使用的是 com.sun.jndi.ldap.LdapCtx 这个内容上下文,那么对应于它的属性配置
        文件就应该是:"com/sun/jndi/ldap/jndiprovider.properties"。
        一个应用可以使用多个这样的属性配置文件。
       
        那么我们为什么要使用属性配置文件?
        有两个原因。第一,我们可以单独的配置某一个命名/服务系统。第二,部署的时候会有用。例如,
        你可以单独配置一个针对LDAP的属性,而不用去修改 jndi.properties,或者增加系统变量。
       
        但是,我们也并不是可以在SPI属性配置文件里设置全部的属性,我们可以设置的属性如下:
       
    java.naming.factory.object
    java.naming.factory.state
    java.naming.factory.control
    java.naming.factory.url.pkgs

        不过并不象 jndi.properties 或者 那个Hastable,SPI属性配置文件里的属性不会自动载入到环境
        里,只有SPI调用了下列方法时才会这样做:
       
    NamingManager.getObjectInstance(Object, Name, Context, Hashtable)
    DirectoryManager.getObjectInstance(Object, Name, Context, Hashtable, Attributes)
    NamingManager.getStateToBind(Object, Name, Context, Hashtable)
    DirectoryManager.getStateToBind(Object, Name, Context, Hashtable, Attributes)
    ControlFactory.getControlInstance(Control, Context, Hashtable)

        例如,假设我们使用一个LDAP服务,它的实现类是com.sun.jndi.ldap.LdapCtx,当这个类调用
        DirectoryManager.getObjectInstance() 方法时,JNDI将会从com/sun/jndi/ldap/jndiprovider.properties
        里找到“java.naming.factory.object”项,然后把它追加到环境里已经定义的项里(例如那个Hashtable
        或者 jndi.properties)。

    posted on 2010-11-24 21:23  aurawing  阅读(566)  评论(0编辑  收藏  举报