在Apache目录服务器中存储Java对象,第2部分:(下)

应用程序 4. 搜索存储的数据

先从 ApacheDS 上的一个简单搜索开始。假设在 ApacheDS 中有许多用户。而您想要找到用户 Alice 的所有详细信息。下面列出了关于 Alice 的所有已知事项:

Alice 是一名用户,所以在用户的数据组织单元中应当可以查找她的数据条目。(在第 1 部分已经介 绍了组织单元或 “ou” 的概念。)

Alice 的用户名是 “alice”(不区分大小写)。

Alice 是一个人,所以她的数据条目使用的对象类必须直接或间接地扩展 person 对象类。

现在请参见清单 6,它显示了名为 SearchForAlice 的应用程序。SearchForAlice 演示了一个非常简 单的搜索场景;后面我将扩展这个应用程序,使其适合更高级的搜索场景。

清单 6. SearchForAlice

public class SearchForAlice {   public SearchForAlice() {     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 InitialDirContext(properties);       //---------------------------------------------       //Step3: Setting search context       //---------------------------------------------       String searchContext = "ou=users";       //--------------------------------------------       //Step4: Creating search attributes for Alice       //--------------------------------------------       Attribute uid = new BasicAttribute("uid");       Attribute bjclass = new BasicAttribute("objectClass");       //adding attribute values       uid.add("Alice");       objclass.add("person");       //Instantiate Attributes object and put search attributes in it.       Attributes attrs = new BasicAttributes(true);       attrs.put(uid);       attrs.put(objclass);       //------------------------------------------       //Step5: Executing search       //------------------------------------------       NamingEnumeration ne = ctx.search(searchContext, attrs);       if (ne != null)       {         //Step 6: Iterating through SearchResults         while (ne.hasMore()) {           //Step 7: Getting individual SearchResult object           SearchResult sr = (SearchResult) ne.next();           //Step 8:           String entryRDN = sr.getName();           System.out.println("RDN of the Searched entry:  "+entryRDN);           //Step 9:           Attributes srAttrs = sr.getAttributes();           if (srAttrs != null) {              //Step 10:              for (Enumeration e = attrs.getAll() ; e.hasMoreElements()  ;)              {                Attribute attr = (Attribute) e.nextElement();                //Step 11:                String attrID = attr.getID();                System.out.println("Attribute Name: "+attrID);                System.out.println("Attribute Value(s):");                NamingEnumeration e1 = attr.getAll();                while (e1.hasMore())                  System.out.println("/t/t"+e1.nextElement());              }//for()           }//if (srAttrs)         }       }//if (ne != null)     } catch (Exception e) {       System.out.println("Operation failed: " + e);     }   }   public static void main(String[] args) {    SearchForAlice searchAlice = new SearchForAlice();   }  }

清单 6 中的搜索应用程序包含 11 个步骤。前两个步骤您应该可以从 清单 1 中回想起来,即装入 JNDI 属性并实例化一个 DirContext 对象。

可以回想一下,在讨论 清单 1 的步骤 1 中名为 java.naming.provider.url 的 JNDI 属性时,我提 到过供应者的 URL 包含两个组件,其中一个是要操作的目录上下文。您可能已经注意到 清单 4 中 java.naming.provider.url 的值是 u=system。ou=system 字符串可在其中进行工作的目录上下文。所 以,所有搜索操作实际上都是在这个目录上下文中执行的。

因为要在这个示例中执行搜索操作,所以可以把 u=system 目录上下文叫做搜索上下文。现在来看一 下搜索应用程序的剩余步骤:

步骤 3. 缩小搜索上下文:已知 Alice 是一个用户,所以不必在整个 u=system 搜索上下文中搜索 Alice,只需在用户的组织单元(即 u=users)中搜索即可。  步骤 4. 创建搜索属性:将关 于 Alice 的已知信息作为搜索属性。因为知道 Alice 的 uid 和对象类,所以可以创建一个只有两个属 性的集合:uid 和 objectClass。可以在 清单 6 的步骤 4 看到这些。(从 第 1 部分 的讨论您应该可 以想起 uid 是 RDN 的组件,而不是属性。但在指定搜索参数时,JNDI 要求指定 uid 值,就像它是属性 一样。)

步骤 5. 执行搜索:在这里调用 清单 6 的步骤 2 得到的 DirContext 对象的 search() 方法。 search() 方法采用了两个参数:第一个参数是在练习的第 3 步创建的搜索上下文,第二个参数是第 4 步的两个属性的集合。search() 方法返回 NamingEnumeration 对象,其中包含搜索结果。

步骤 1 到 5 设置搜索操作。其余的步骤处理 NamingEnumeration 对象并提取搜索结果。

步骤 6. 提取搜索结果:清单 6 的步骤 6 中的 NamingEnumeration 对象包含搜索结果集合。集合中 的每个搜索结果都由一个 SearchResult 对象表示。要提取单个的搜索结果,只需在 NamingEnumeration 对象上进行迭代即可。

步骤 7. 处理单个搜索结果:注意,每个搜索结果都包含关于单一数据条目的信息。可以从 SearchResult 对象得到数据条目的两部分信息(即 RDN 和它的所有属性)。

步骤 8. 调用 getName():SearchResult 对象的 getName() 方法返回所搜索条目的 RDN。Alice 的 RDN 是 uid=alice。

步骤 9. 调用 getAttributes():SearchResult 对象的 getAttributes() 方法返回 Attributes 对 象,它包含与所搜索条目相关的所有属性值。Attributes 对象表示的属性集合与 清单 6 的步骤 4 中创 建的属性集合类似。

步骤 10. 调用 getAll():Attributes 对象的 getAll() 方法返回一个枚举,其中包含集合中的所有 属性。

步骤 11. 处理属性:最后,从集合中取出一个属性,并调用其 getID() 和 getAll() 方法。getID() 方法以字符串形式返回属性的名称。getAll() 方法以枚举的形式返回所有属性值。

应用程序 5. 按名称搜索

在前面的搜索示例中,我们已查看了如何在知道用户 uid 时搜索用户。在这个示例中,将学习如何修 改应用程序,用 Alice 的用户名而不是 uid 来搜索她。

回忆一下 第 1 部分的图 18,在该图中,用户名存储为用户数据条目的 cn 属性的值。所以,在这个 应用程序中,您将搜索 cn 属性,如清单 7 所示:

清单 7. SearchForAliceByCN

public class SearchForAliceByCN {   public SearchForAliceByCN() {     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 InitialDirContext(properties);       //---------------------------------------------       //Step3: Setting search context       //---------------------------------------------       String searchContext = "ou=users";       //--------------------------------------------       //Step4: Creating search attributes for Alice       //--------------------------------------------       Attribute cn = new BasicAttribute("cn");       Attribute bjclass = new BasicAttribute("objectClass");       //putting attribute values       cn.add("Alice");       objclass.add("person");       //Instantiate an Attributes object and put search attributes in  it       Attributes attrs = new BasicAttributes(true);       attrs.put(cn);       attrs.put(objclass);       //------------------------------------------       //Step5: Executing search       //------------------------------------------       NamingEnumeration ne = ctx.search(searchContext, attrs);       if (ne != null)       {         //Step 6: Iterating through SearchResults         while (ne.hasMore()) {           //Step 7: Getting individual SearchResult object           SearchResult sr = (SearchResult) ne.next();           //Step 8:           String entryRDN = sr.getName();           //Step 9:           Attributes srAttrs = sr.getAttributes();           if (srAttrs != null) {              //Step 10:              for (Enumeration e = attrs.getAll() ; e.hasMoreElements()  ;)              {                Attribute attr = (Attribute) e.nextElement();                //Step 11:                String attrID = attr.getID();                System.out.println("Attribute Name: "+attrID);                System.out.println("Attribute Value(s):");                NamingEnumeration e1 = attr.getAll();                 while (e1.hasMore())                   System.out.println("/t/t"+e1.nextElement());              }//for()           }//if (srAttrs)         }       }//if (ne != null)     } catch (Exception e) {       System.out.println("Operation failed: " + e);     }   }   public static void main(String[] args) {     SearchForAliceByCN searchAlice = new SearchForAliceByCN();   }  }

SearchForAliceByCN 应用程序显示了用 Alice 的用户名搜索她的步骤。应用程序与前面的 SearchForAlice 应用程序非常相似,只有一个区别。在 清单 6 的步骤 4 中,我们为便于搜索创建了一 个由 uid 和 objectClass 属性组成的集合。而在这个 应用程序的步骤 4 中,创建的是 cn 和 objectClass 属性的集合。

关于匹配规则

搜索 cn 有一个要点需要注意。在 第 1 部分的图 13 可以看到,cn 属性类型有一个叫做 SUBSTR 的 字段,它定义了子字符串匹配的匹配规则。

在 cn 属性的示例中,SUBSTR 字段的值是 caseIgnoreMatch,所以在搜索 cn 属性的特定值时,即使 搜索的名称只与 cn 属性值的子字符串匹配,也认为匹配成功。而且,子字符串匹配是不区分大小写的。

所以,如果搜索 “alice”,那么所有名字、中间名或姓为 “Alice” 的用户都会包含在搜索结果中 。

应用程序 6. 反序列化 Java 对象

前面已经看到了如何在 ApacheDS 中存储 Java 对象并搜索与存储的对象有关的属性。现在将学习如 何搜索和反序列化 Java 对象。反序列化是序列化的反向操作,在这里将根据 Java 对象的序列化形式创 建一个 Java 对象。

清单 8 所示的应用程序搜索并反序列化 Alice 的 MessagingPreferences 对象。请回想一下 清单 1 中存储在 ApacheDS 中的 MessagingPreferences 对象。

FetchAliceMessagingPreferences 应用程序是 清单 7 中看到的 SearchForAliceByCN 应用程序的增 强版。实际上,清单 8 与 清单 7 在步骤 8 之前都相同,在步骤 8 中,将提取 Alice 数据条目的 RDN 。从清单 8 中的步骤 8 中,将开始查找 Alice 的 Preferences 对象:

清单 8. FetchAliceMessagingPreferences

public class FetchAliceMessagingPreferences {   public FetchAliceMessagingPreferences() {     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 InitialDirContext(properties);       //---------------------------------------------       //Step3: Setting search context       //---------------------------------------------       String searchContext = "ou=users";       //--------------------------------------------       //Step4: Creating search attributes for Alice       //--------------------------------------------       Attribute cn = new BasicAttribute("cn");       Attribute bjclass = new BasicAttribute("objectClass");       //putting attribute values       cn.add("Alice");       objclass.add("person");       //Instantiate an Attributes object and put search attributes in  it       Attributes attrs = new BasicAttributes(true);       attrs.put(cn);       attrs.put(objclass);       //------------------------------------------       //Step5: Executing search       //------------------------------------------       NamingEnumeration ne = ctx.search(searchContext, attrs);       if (ne != null)       {         //Step 6: Iterating through SearchResults         while (ne.hasMore()) {           //Step 7: Getting individual SearchResult object           SearchResult sr = (SearchResult) ne.next();           //Step 8:           String entryRDN = sr.getName();           //---------------------------------------------           //Step9: Setting a new search context           //---------------------------------------------           searchContext = entryRDN + "," + searchContext;           //---------------------------------------------           //Step10: Creating search controls           //---------------------------------------------           SearchControls ctls = new SearchControls();           ctls.setReturningObjFlag(true);           ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);           //---------------------------------------------           //Step11: Creating filter           //---------------------------------------------           String filter = "(|(javaClassName=MessagingPreferences)                     (javaClassName=ShippingPreferences))";           //------------------------------------------           //Step12: Executing search           //------------------------------------------           NamingEnumeration ne1 = ctx.search(searchContext, filter,  ctls);           if (ne != null)           {             //Step13: Iterating through SearchResults             while (ne1.hasMore()) {               //Step14: Getting individual SearchResult object               SearchResult sr1 = (SearchResult) ne1.next();               //Step15: Getting preferences object               Object bj = sr1.getObject();               if (obj != null) {                 if (obj instanceof MessagingPreferences){                   MessagingPreferences pref =                    (MessagingPreferences) obj;                 }                 else if (obj instanceof ShippingPreferences) {                  ShippingPreferences pref = (ShippingPreferences)  obj;                 }               }             }//while(           }//if         }//while       }//if (ne != null)     } catch (Exception e) {       System.out.println("Operation failed: " + e);     }   }   public static void main(String[] args) {     FetchAliceMessagingPreferences searchAlicePreferences =        new FetchAliceMessagingPreferences();   }  }

搜索已访问过的上下文

在进入搜索和反序列化 Alice 的 MessagingPreferences 对象的步骤之前,您可能想回顾一下 图 1 ,该图显示了位于 Alice 数据条目中的 Alice 的 MessagingPreferences 对象。所以,需要在 Alice 的数据条目中查找 MessagingPreferences 对象。

如何在数据条目 “内部进行查找” 呢?针对这一目的,我们将明确使用搜索上下文 的概念。在这个 示例中,需要缩小在第一个应用程序(清单 6 所示的 SearchForAlice)中介绍的搜索上下文。

这项工作在 清单 8 的步骤 9 进行,在这一步骤中将把 Alice 的 RDN(uid=alice)与原来用于搜索 Alice 数据条目的上下文(ou=users,ou=system)合并在一起。生成的上下文 (uid=alice,ou=users,ou=system)就可以用来查看 Alice 数据条目内部。

现在来看一下 FetchAliceMessagingPreferences 应用程序剩下的步骤。

构建和使用搜索控件

在 清单 8 的步骤 10 中,实例化了一个 SearchControls 对象,可以用它构建一些搜索控件。搜索 控件有两个主要用途:

指定搜索结果中包含的数据类型。在这个示例中,因为您想要检索 Java 对象,所以将调用 SearchControls 对象的 setReturningObjFlag() 方法。这个方法在搜索控件中设置标志,以指定将要执 行的获得某一对象的搜索操作。

指定搜索范围。“搜索范围” 意味着您想在特定数据条目中搜索,还是还想在比该条目更低的级别上 进行搜索。通过调用 SearchControls 对象的 setSearchScope() 方法,可以设置搜索范围。

过滤搜索结果

在 清单 8 的步骤 11 中,制作了名为 “filter” 的字符串。可以看到 filter 字符串的值是 (| (javaClassName=MessagingPreferences) (javaClassName=ShippingPreferences))。括号中的这两个属 性-值对为一个名为 javaClassName 的属性指定了不同的值。还请注意这两个属性-值对之前的 “OR”。 这意味着要查询的是 MessagingPreferences 或 ShippingPreferences 对象。

这个 filter 字符串充当搜索结果的过滤器。这意味着搜索操作之后返回的搜索结果将只包含符合搜 索过滤器指定标准的结果。

在寻找大量属性值时要使用这类搜索过滤器。

提取和处理搜索结果

在 清单 8 的步骤 12 中,您可以调用 search() 方法,并随该方法调用一起传递搜索上下文、搜索 过滤器和搜索控件。

请注意 清单 6 的步骤 5 和 清单 8 中使用的搜索方法调用之间的区别:在清单 6 中,使用的是带 两个参数的 search() 方法,而在 清单 8 中,使用的是该方法的带三个参数的形式。

清单 8 中的步骤 13 和 14 分别与 清单 7 中的步骤 6 和 7 相同。在这些步骤中,处理 NamingEnumeration 对象,并以 SearchResult 对象的形式提取单个的搜索结果。

最后,在步骤 15,调用 SearchResult 对象的 getObject() 方法。getObject() 方法返回一个 Java 对象。

对于 getObject() 方法返回的实例属于哪个类,没法确定,因为在搜索请求中指定了两个类 (MessagingPreferences 或 ShippingPreferences)。返回的结果可能是其中一个类的实例。所以,首 先必须检查对象属于哪个实例,并进行相应的类型转换。检查并转换之后,Java 对象就处于控制之下, 然后就可以调用它的方法了。

应用程序 7. 将 Java 对象解编

在前面的应用程序中,学习了如何反序列化已经序列化的 Java 对象。接下来,将通过应用程序 FetchAliceMarshalledPreferences 学习如何解编一个已编组的 Java 对象。

清单 9. FetchAliceMarshalledPreferences

public class FetchAliceMarshalledPreferences {   public FetchAliceMarshalledPreferences() {     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 InitialDirContext(properties);       //---------------------------------------------       //Step3: Setting search context       //---------------------------------------------       String searchContext = "ou=users";       //--------------------------------------------       //Step4: Creating search attributes for Alice       //--------------------------------------------       Attribute cn = new BasicAttribute("cn");       Attribute bjclass = new BasicAttribute("objectClass");       //putting attribute values       cn.add("Alice");       objclass.add("person");       //Instantiate an Attributes object and put search attributes in  it       Attributes attrs = new BasicAttributes(true);       attrs.put(cn);       attrs.put(objclass);       //------------------------------------------       //Step5: Executing search       //------------------------------------------       NamingEnumeration ne = ctx.search(searchContext, attrs);       if (ne != null)       {         //Step 6: Iterating through SearchResults         while (ne.hasMore()) {           //Step 7: Getting individual SearchResult object           SearchResult sr = (SearchResult) ne.next();           //Step 8:           String entryRDN = sr.getName();           System.out.println("RDN of the Searched entry:  "+entryRDN);           //---------------------------------------------           //Step9: Setting a new search context           //---------------------------------------------           searchContext = entryRDN + "," + searchContext;           System.out.println("new SearchContext: "+searchContext);           //---------------------------------------------           //Step10: Creating search controls           //---------------------------------------------           SearchControls ctls = new SearchControls();           ctls.setReturningObjFlag(true);           ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);           //---------------------------------------------           //Step11: Creating filter           //---------------------------------------------           String filter =  "(javaClassName=java.rmi.MarshalledObject)";           //------------------------------------------           //Step12: Executing searchl           //------------------------------------------           NamingEnumeration ne1 = ctx.search(searchContext, filter,  ctls);           if (ne != null)           {             //Step13: Iterating through SearchResults             while (ne1.hasMore()) {               //Step14: Getting individual SearchResult object               SearchResult sr1 = (SearchResult) ne1.next();               //Step15: Getting preferences object               Object bj = sr1.getObject();               if (obj instanceof MarshalledObject) {                 MarshalledObject mObj= (MarshalledObject) obj;                 Object obj1 = mObj.get();                 if (obj1 instanceof MarshalledObject) {                   MessagingPreferences pref =                    (MessagingPreferences) obj;                 }                 else if (obj1 instanceof ShippingPreferences) {                   ShippingPreferences pref =                    (ShippingPreferences) obj;                 }               }//if(obj)             }//while           }//if          }//while       }//if (ne != null)     } catch (Exception e) {       System.out.println("Operation failed: " + e);     }   }   public static void main(String[] args) {     FetchAliceMarshalledPreferences fetchAlicePref =       new FetchAliceMarshalledPreferences();   }  }

清单 9 包含 15 个步骤,前 14 个步骤完全与 清单 8 中的前 14 个步骤相同。两个应用程序的惟一 区别是 清单 9 的步骤 15,在这一步骤中,SearchResult.getObject() 返回一个已编组的对象。您自己 可以检查返回对象的名称,对这一点进行验证。在完成检查之后,要将对象类型转换为 MarshalledObject。

Java 对象的序列化形式位于这个编组对象的内部,所以现在要调用 MarshalledObject 类的 get() 方法获得 Java 对象,然后可以在确定对象的类之后进行类型转换。

应用程序 8. 编辑和更新存储的对象

在目前为止的示例中,我的重点都放在存储和搜索 Java 对象上。下一个应用程序将显示如何编辑和 更新已经存储在 ApacheDS 中的 Java 对象。

清单 10 中的 UpdateAlicePreferences 应用程序更新的 Java 对象是在 清单 4 中与 person 对象 类混合的对象:

清单 10. UpdateAlicePreferences

public class UpdateAlicePreferences {   public UpdateAlicePreferences() {     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 InitialDirContext(properties);       //---------------------------------------------       //Step3: Setting search context       //---------------------------------------------       String searchContext = "ou=users";       //--------------------------------------------       //Step4: Creating search attributes for Alice       //--------------------------------------------       Attribute cn = new BasicAttribute("cn");       Attribute bjclass = new BasicAttribute("objectClass");       //putting attribute values       cn.add("Alice");       objclass.add("person");       //Instantiate an Attributes object and put search attributes in  it       Attributes attrs = new BasicAttributes(true);       attrs.put(cn);       attrs.put(objclass);       //------------------------------------------       //Step5: Executing search       //------------------------------------------       NamingEnumeration ne = ctx.search(searchContext, attrs);       if (ne != null)       {         //Step 6: Iterating through SearchResults         while (ne.hasMore()) {           //Step 7: Getting individual SearchResult object           SearchResult sr = (SearchResult) ne.next();           //Step 8:           String entryRDN = sr.getName();           //---------------------------------------------           //Step9: Setting a new search context           //---------------------------------------------           searchContext = entryRDN + "," + searchContext;           //---------------------------------------------           //Step10: Creating search controls           //---------------------------------------------           SearchControls ctls = new SearchControls();           ctls.setReturningObjFlag(true);           ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);           //---------------------------------------------           //Step11: Creating filter           //---------------------------------------------           String filter =  "(javaClassName=java.rmi.MarshalledObject)";           //------------------------------------------           //Step12: Executing search           //------------------------------------------           NamingEnumeration ne1 = ctx.search(searchContext, filter,  ctls);           if (ne != null)           {             //Step13: Iterating through SearchResults             while (ne1.hasMore()) {               //Step14: Getting individual SearchResult object               SearchResult sr1 = (SearchResult) ne1.next();               //Step15: Getting preferences object               Object bj = sr1.getObject();               entryRDN = sr1.getName();               if (obj != null && obj instanceof  MarshalledObject)               {                 MarshalledObject mObj= (MarshalledObject) obj;                 MessagingPreferences pref =                  (MessagingPreferences) mObj.get();                 //Step16: Updating your java object                 pref.setStyles ("http://www.mystyles.com/preferences");                 //Step17: Setting a new context for data updation                   String bindContext = entryRDN + "," +  searchContext;                 //Step18: Reading attributes in a new collection                   Attributes attrs1 = sr1.getAttributes();                 //Step19:Updating data                  ctx.rebind(bindContext, pref, attrs);                 System.out.println("Updating                   the MessagingPreferences succeed");               }             }//while           }//if         }//while       }//if (ne != null)     } catch (Exception e) {       System.out.println("Operation failed: " + e);     }   }   public static void main(String[] args) {     UpdateAlicePreferences updateAlicePreferences =       new UpdateAlicePreferences();   }  }

清单 10 的前 15 个步骤与前面的示例类似。在这些步骤中,只是搜索并提取要更新的 Java 对象。 在后面的步骤中,将更新和存储对象。

更新和存储 Java 对象

在步骤 16 中,通过调用 Java 对象上的方法,更新 Java 对象中包含的数据。数据更新之后,需要 存储 Java 对象编辑过的版本。在执行这个操作之前,需要做两件事:

获得命名上下文:更新现有条目,这意味着您想把已更新的数据写入它以前所在的命名上下文中。要 做到这一点,则需要知道已更新条目的命名上下文。将搜索上下文(用来搜索对象的上下文)与对象的 RDN 结合,就可以形成 Java 对象的命名上下文。在 清单 10 的步骤 17 中可以看到这点。

将所有属性写入已更新的 Java 对象:在将更新的 Java 对象写回 ApacheDS 时,是在同一命名上下 文上写入新的数据条目。所有与数据条目有关的属性都将丢失。所以,需要将数据条目的所有属性写回已 更新的 Java 对象。在清单 10 的步骤 18 中,我的处理是从搜索结果读取所有属性,并将它们包装在属 性集合中。

最后,在步骤 19 中调用 DirContext.rebind() 方法。rebind() 方法接收的参数就是在 SToreBobPreferences 应用程序的 清单 4 的步骤 4 中传递给带有三个参数的 bind() 方法的参数。

bind() 和 rebind() 方法之间惟一的区别是 rebind() 方法将数据条目存储在现有数据条目已经占据 的命名上下文中,从而有效地用新数据更新现有条目。

应用程序 9. 综合在一起

总结这篇文章时,我将用最后一个应用程序把迄今为止学到的所有概念都组合到一个简单的、可重用 的对象中,这个对象可以在 ApacheDS 中存储、搜索、删除和更新 Java 对象。LDAP4JavaObjects 类如 清单 11 所示:

清单 11. LDAP4JavaObjects

public class LDAP4JavaObjects {   protected String commonName = null;   protected String surName = null;   protected String userID = null;   protected String javaObjectCN = null;   protected String userName = null;   protected String password = null;   protected String initialContext = null;   private String workingContext = null;   protected DirContext dirContext = null;   protected Properties properties = null;   protected Object javaObject = null;   protected Attributes attributes = null;   public LDAP4JavaObjects() {     properties = new Properties();     attributes = new BasicAttributes(true);     workingContext = "ou=users";     initialContext = workingContext;     try {       InputStream inputStream = new FileInputStream ( "ApacheDS.properties");       properties.load(inputStream);     } catch (Exception e) {       e.printStackTrace();     }   }//LDAPJavaObjects   public void setUserName(String userName){     properties.setProperty("java.naming.security.principal", userName);     this.userName = userName;   }   public void setPassword(String password) {     properties.setProperty("java.naming.security.credentials", password);     this.password = password;   }   protected void connect() {     try {       // Fetch the direcTory context.       dirContext= new InitialDirContext(properties);     } catch (NamingException e) {       System.out.println("Getting initial context operation failed: " +  e);     }   }   protected void close() {     try {       // Close the direcTory context.       dirContext.close();     } catch (NamingException e) {       System.out.println("Closing initial context operation failed: " +  e);     }   }   public void setJavaObject(Object obj) throws  java.io.NotSerializableException {     if (obj instanceof java.io.Serializable)       javaObject = obj;   }   protected boolean isObjectPresent(String uid, String cn, String  workContext) {     NamingEnumeration ne = search(uid, cn, workContext);     if (ne != null)       return true;     else       return false;   }//isObjectPresent   protected NamingEnumeration search(String user, String object, String  workContext) {     NamingEnumeration ne = null;     String filter = new String();     SearchControls ctls = new SearchControls();     ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);     try {       if (user != null && bject == null) {         addUserAttribute ("uid", user);         addUserAttribute ("objectClass", "person");         ne = dirContext.search(workContext, attributes);         if (ne != null && ne.hasMore())           return ne;         else           return null;       }else if (user == null && object != null)       {         filter = "(cn="+object+")";         ctls.setReturningObjFlag(true);         ne = dirContext.search(workContext, filter, ctls);         if (ne != null && ne.hasMore())           return ne;         else           return null;       }     } catch (NamingException e) {       e.printStackTrace();     }     return null;   }//search   protected void update(String uid, String cn, String workContext) {     if (uid != null && cn == null)     {       String[] bjClassValues = {"top","person"};       addUserAttribute ("uid", uid);       if (commonName != null)         addUserAttribute ("cn", commonName);       addUserAttributes ("objectClass", objClassValues);       try {         dirContext.rebind(workContext, null, attributes);       } catch (NamingException e) {         System.out.println("SToring operation failed: " + e);       }     } else if (uid == null && cn != null) {       addUserAttribute ("cn", cn);       try {         dirContext.rebind(workContext, javaObject, attributes);       } catch (NamingException e) {         e.printStackTrace();       }     }   }//update   protected void sTore(String uid, String cn, String workContext) {     if (uid != null && cn == null)     {       String[] bjClassValues = {"top","person"};       addUserAttribute ("uid", uid);       if (commonName != null)         addUserAttribute ("cn", commonName);       addUserAttributes ("objectClass", objClassValues);       try       {         dirContext.bind(workContext, null, attributes);       } catch (NamingException e) {         e.printStackTrace();       }     } else if (uid == null && cn != null) {       addUserAttribute ("cn", cn);       try {         dirContext.bind(workContext, javaObject, attributes);       } catch (NamingException e) {         e.printStackTrace();       }     }   }//sTore   public void addUserAttribute (String name, String value){     Attribute attr = new BasicAttribute(name);     attr.add(value);     attributes.put(attr);   }//addUserAttribute   public void addUserAttributes(String name, String[] value) {     Attribute attr = new BasicAttribute (name);     if (value != null) {       for (int i =0; i < value.length; i++)         attr.add(value[i]);     }     attributes.put(attr);   }//addUserAttribute   public void writeToServer () {     connect();     if (userID == null && commonName != null)       userID = commonName;     //Check if the cn of the Java object is set.     //If not, use commonName as object cn.     if (javaObject != null)     {       if (javaObjectCN == null && commonName != null)         javaObjectCN = commonName;     }     if (!(isObjectPresent(userID, null, initialContext)))     {      workingContext = "uid="+userID+","+initialContext;      sTore(userID, null, workingContext);      workingContext = "cn="+javaObjectCN  +",uid="+userID+","+initialContext;      sTore( null, javaObjectCN, workingContext);     } else if (isObjectPresent(null, javaObjectCN,      "uid="+userID+","+initialContext)) {      workingContext = "cn="+javaObjectCN +",uid="+userID +","+  initialContext;      update(null, javaObjectCN, workingContext);     } else {      workingContext = "cn="+javaObjectCN +",uid="+userID +","+  initialContext;      sTore(null, javaObjectCN, workingContext);     }     close();   }//writeToServer()   public void searchFromServer(String user, String object) {     connect();     NamingEnumeration ne = search(user, object, initialContext);     if (ne != null)       processSearchResult(ne);     else       System.out.println ("searchFromServer... failed");     close();   }//searchFromServer   private void processSearchResult (NamingEnumeration ne){     try {       if (ne!=null)       {         while (ne.hasMore()) {           SearchResult sr = (SearchResult) ne.next();           Object bj = sr.getObject();           if (obj != null) {             javaObject = obj;             javaObjectCN = sr.getName();           } else {             userID = sr.getName();             processAttributes(sr.getAttributes());           }         }//while       }//if (ne != null)     } catch (javax.naming.NamingException e) {       e.printStackTrace();     }   }//processSearchResult   private void processAttributes(Attributes attrs){     try {       for (Enumeration e = attrs.getAll() ; e.hasMoreElements() ;)  {         Attribute attr = (Attribute) e.nextElement();         if (attr.getID().equals("cn"))           commonName = (String)attr.get();         else if (attr.getID().equals("sn"))           surName = (String)attr.get();       }     } catch (javax.naming.NamingException ne){       ne.printStackTrace();     }   }//processAttributes   public void setUserID(String userID) {     this.userID = userID;   }   public String getUserID() {     return userID;   }   public void setJavaObjectCN(String objectCN) {     this.javaObjectCN = objectCN;   }   public String getJavaObjectCN() {     return javaObjectCN;   }   public void setCommonName (String cn) {     commonName = cn;   }   public void setSurName (String sn) {     surName = sn;   }   public String getCommonName() {     return commonName;   }   public String getSurName() {     return surName;   }   public static void main(String[] args) {     LDAP4JavaObjects javaLdapClient = new LDAP4JavaObjects();     javaLdapClient.setPassword("secret");     javaLdapClient.setUserID("Alice");     //Instantiating a Java object.     MessagingPreferences msgPreferences =       new MessagingPreferences ();     //Setting a Java object.     try {       javaLdapClient.setJavaObject (msgPreferences);     } catch (Exception e) {       e.printStackTrace();     }     javaLdapClient.setJavaObjectCN ("preferences");     javaLdapClient.writeToServer();     System.out.println ("Entry sTored in ApacheDS..");   }//main  }

应用程序说明

关于 LDAP4Java 类您会注意到的第一件事是:对于属于 person 对象类的用户,它拥有管理用户数据 条目的方法。setUserName() 和 setPassword() 方法设置应用程序的用户名和口令。

在设置服务器地址之后,可以使用诸如 setCommonName() 和 setSurName() 之类的 setter 方法。这 些 setter 方法用于设置用户的属性值。除了 setCommonName() 和 setSurName() 方法之外, LDAP4JavaObjects 还包含 addUserAttribute() 方法。可以用 addUserAttribute() 方法指定额外的用 户属性。

在设置属性值之后,可以调用 setJavaObject() 方法在 LDAP4JavaObjects 中设置 Java 对象。还可 以用 setContext() 方法设置想在其中编写或搜索 Java 对象的服务器上下文。

为 LDAP4JavaObjects 提供所有需要的数据后,可以调用 writeToServer() 方法。这个方法是智能的 。它首先查看用户条目在远程服务器上是否存在。如果不能找到该条目,则编写一个新条目。如果条目已 经存在于服务器上,则更新该条目。

LDAP4JavaObjects 还有一个方法叫做 searchFromServer(),可以用它在 ApacheDS 中搜索用户数据 或 Java 对象。searchFromServer() 方法在完成搜索操作之后更新 LDAP4JavaObjects 数据成员。

可以扩展 LDAP4JavaObjects 应用程序来适应您自己的应用程序的业务逻辑。所有底层 JNDI 逻辑都 使用 LDAP4JavaObjects 的 protected 方法进行编写就是出于这个原因。可以在扩展您自己的 LDAP4JavaObjects 的类中使用 protected 方法。

如果想查看使用这些 LDAP4JavaObjects 方法的演示,可以运行清单 11 中的 main()。

第 2 部分结束语

在这个由两部分组成的系列中,我们了解了 ApacheDS 如何提供服务器端功能。除了关于 ApacheDS 的目录服务架构以及如何将它用作 LDAP 服务器的背景讨论之外,我还提供了 9 个示例应用程序,演示 了在 ApacheDS 中如何存储、搜索、检索和更新 Java 对象。

本文的 完整源代码 中包括 JNDI 代码示例,可以用它来处理 ApacheDS 中的 Java 对象,源代码中 还包含一个可重用类,该类包含已讨论的所有功能。您可以使用这个类来练习操作 ApacheDS 中的 Java 对象,也可以在自己的 Java 应用程序中扩展它。

而只有在充满了艰辛的人生旅途中,

在Apache目录服务器中存储Java对象,第2部分:(下)

相关文章:

你感兴趣的文章:

标签云: