上海網(wǎng)站備案北京發(fā)生大事了
命名和目錄操作
您可以使用JNDI執(zhí)行以下操作:讀取操作和更新命名空間的操作。本節(jié)介紹這兩個操作:
l?????????查詢對象
l?????????列出上下文內(nèi)容
l?????????添加、覆蓋和移除綁定
l?????????重命名對象
l?????????創(chuàng)建和銷毀子上下文
配置
在命名和目錄服務(wù)中執(zhí)行操作之前,需要得到初始化上下文――命名空間的開始點(diǎn)。因為命名和目錄服務(wù)的所有方法都相對于一些上下文執(zhí)行。
為了得到初始化上下文,必須執(zhí)行以下步驟:
1.?????選擇想要訪問的訪問提供者。
2.?????指定需要的初始化上下文。
3.?????調(diào)用InitialContext構(gòu)造函數(shù)。
第一步:為初始化上下文選擇服務(wù)提供者
您可以為初始化上下文指定服務(wù)提供者,創(chuàng)建一個環(huán)境變量集合(Hashtable),同時將服務(wù)提供者的名稱加入其中。環(huán)境屬性在JNDI教程中有詳細(xì)的介紹。
如果您使用Sun的LDAP服務(wù)提供者,代碼如下所示:
?
Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); |
?
要指定Sun的文件系統(tǒng)服務(wù)提供者,代碼如下所示:
?
Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.fscontext.RefFSContextFactory"); |
?
您可以使用一些系統(tǒng)屬性描述使用的服務(wù)提供者。在JNDI教程中有詳細(xì)描述。
第二步:提供初始化上下文需要的信息
不同目錄的客戶端可能需要提供不同的信息用來連接目錄。例如,您需要指定服務(wù)器運(yùn)行的機(jī)器以及識別目錄中的用戶。這些信息通過環(huán)境屬性傳遞給服務(wù)提供者。JNDI指定服務(wù)提供者使用的一般環(huán)境參數(shù)。您的服務(wù)提供者文檔會為需要提供的參數(shù)進(jìn)行詳細(xì)的說明。
LDAP提供者需要程序提供LDAP服務(wù)器的位置,以及認(rèn)證信息。要提供這些信息,需要如下代碼:
?
env.put(Context.PROVIDER_URL, "ldap://ldap.wiz.com:389"); env.put(Context.SECURITY_PRINCIPAL, "joeuser"); env.put(Context.SECURITY_CREDENTIALS, "joepassword"); |
?
本教程中使用Sun的LDAP服務(wù)提供者。例子中假設(shè)服務(wù)器設(shè)置在本機(jī),使用389端口,根辨別名是“o=JNDITutorial”,修改目錄不需要認(rèn)證。這些信息是設(shè)置環(huán)境所需要的。
?
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial"); |
?
如果您使用不同設(shè)置的目錄,需要設(shè)置相應(yīng)的環(huán)境屬性。您需要使用機(jī)器名稱替換“l(fā)ocalhost”。您可以在任何公共的目錄服務(wù)器或在其他機(jī)器上的自己的服務(wù)器運(yùn)行例子。您需要將“l(fā)ocalhost”替換成那臺機(jī)器的名字,將o=JNDITutorial替換成相應(yīng)的命名上下文。
第三步:創(chuàng)建初始化上下文
您已經(jīng)創(chuàng)建了初始化上下文。為了做到這一點(diǎn),您將之前創(chuàng)建的環(huán)境屬性放到InitialContext構(gòu)造函數(shù)中:
?
Context ctx = new InitialContext(env); |
?
現(xiàn)在您有了一個上下文對象的引用,可以開始訪問命名服務(wù)了。
為了完成目錄操作,需要使用InitialDirContext。為了做到這一點(diǎn),使用它的一個構(gòu)造函數(shù):
?
DirContext ctx = new InitialDirContext(env); |
?
這句話返回了用來進(jìn)行目錄操作的DirContext對象引用。
命名異常
JNDI包中的很多方法當(dāng)拋出異常時,說明操作請求不能執(zhí)行。一般情況下,您將看到可以拋出NamingException的方法使用try/catch進(jìn)行包裝。
?
try { ????Context ctx = new InitialContext(); ????Object obj = ctx.lookup("somename"); } catch (NamingException e) { ????// Handle the error ????System.err.println(e); } |
?
異常類結(jié)構(gòu)
JNDI有豐富的異常結(jié)構(gòu),所有異常都從NamingException類中繼承。異常類名都是自解釋的,在下文中進(jìn)行列舉。
如果要處理特定的NamingException子類,需要分別catch子類。例如,以下代碼特別的對待AuthenticationException及其子類。
?
try { ????Context ctx = new InitialContext(); ????Object obj = ctx.lookup("somename"); } catch (AuthenticationException e) { ????// attempt to reacquire the authentication information ????... } catch (NamingException e) { ????// Handle the error ????System.err.println(e); } |
?
枚舉
諸如Context.list()和DirContext.search()這種操作返回NamingEnumeration。在這些情況下,如果出現(xiàn)錯誤并且沒有返回結(jié)果,NamingException或它的子類會在方法請求時拋出。如果出現(xiàn)錯誤并且返回了部分結(jié)果,返回NamingEnumeration這樣您可以取得這些結(jié)果。當(dāng)所有結(jié)果都取出來后,再請求NamingEnumeration.hasMore()會導(dǎo)致拋出NamingException(或其子類)異常,表示出現(xiàn)錯誤。在這種情況下,枚舉變得非法并且不能再請求其中任何方法。
例如,如果執(zhí)行search()并且指定最多返回多少結(jié)果,那么search()最多返回n個結(jié)果。如果結(jié)果超過n個,那么當(dāng)?shù)趎+1次請求NamingEnumeration.hasMore()時,拋出SizeLimitExceededException。請參見本節(jié)中的有關(guān)limit的示例代碼。
本手冊中的例子
在本手冊文件中的在線示例代碼中,通常為了便于閱讀省略了try/catch語句。通常,因為只有部分代碼片段在這里展示,所以只展示直接表示概念的行。如果您查看本教程附帶的源碼文件,將看到try/catch語句的合適位置。
javax.naming包中異常在這里可以看到。
查詢對象
要從命名服務(wù)中查詢對象,使用Context.lookup()方法并且傳入您想取得的對象名。假設(shè)命名服務(wù)中有一個對象的名稱是cn=Rosanna Lee,ou=People。要取得這個對象,您只需要編寫:
?
Object obj = ctx.lookup("cn=Rosanna Lee,ou=People"); |
?
lookup()返回的對象類型依賴于命名服務(wù)以及對象關(guān)聯(lián)的數(shù)據(jù)。命名服務(wù)可以包含許多不同類型的對象,同時在系統(tǒng)的不同部分查詢的對象可能得到不同的類型。例如,“cn=Rosanna Lee,ou=People”綁定到上下文對象中(javax.naming.ldap.LdapContext)。您可以對lookup()方法的結(jié)果cast成需要的類。
例如,以下代碼查詢“cn=Rosanna Lee,ou=People”對象并且cast成LdapContext。
?
import javax.naming.ldap.LdapContext; ... LdapContext ctx = (LdapContext) ctx.lookup("cn=Rosanna Lee,ou=People"); |
?
完整的例子在Lookup.java文件中。
在Java SE 6中查詢名稱引入了兩個新的靜態(tài)方法:
l?????????InitialContext.doLookup(Name name)
l?????????InitialContext.doLookup(String name)
?
這些方法提供了不實例InitialContext查找對象的快捷方式。
列舉上下文
代替Context.lookup()一次取得一個對象的方法,您可以在一個單一的操作中列舉整個剩下文。列舉上下文有兩個方法:一個返回了綁定關(guān)系,另一個只返回名-對象類型名。
Context.List()方法
Context.list()返回NameClassPair的枚舉。每個NameClassPair包含對象名和對象類型名。下列代碼列舉了“ou=People”目錄的內(nèi)容(例如,“ou=People”目錄中找到的文件和目錄)。
?
NamingEnumeration list = ctx.list("ou=People"); ? while (list.hasMore()) { ????NameClassPair nc = (NameClassPair)list.next(); ????System.out.println(nc); } |
?
返回值如下:
?
# java List cn=Jon Ruiz: javax.naming.directory.DirContext cn=Scott Seligman: javax.naming.directory.DirContext cn=Samuel Clemens: javax.naming.directory.DirContext cn=Rosanna Lee: javax.naming.directory.DirContext cn=Maxine Erlund: javax.naming.directory.DirContext cn=Niels Bohr: javax.naming.directory.DirContext cn=Uri Geller: javax.naming.directory.DirContext cn=Colleen Sullivan: javax.naming.directory.DirContext cn=Vinnie Ryan: javax.naming.directory.DirContext cn=Rod Serling: javax.naming.directory.DirContext cn=Jonathan Wood: javax.naming.directory.DirContext cn=Aravindan Ranganathan: javax.naming.directory.DirContext cn=Ian Anderson: javax.naming.directory.DirContext cn=Lao Tzu: javax.naming.directory.DirContext cn=Don Knuth: javax.naming.directory.DirContext cn=Roger Waters: javax.naming.directory.DirContext cn=Ben Dubin: javax.naming.directory.DirContext cn=Spuds Mackenzie: javax.naming.directory.DirContext cn=John Fowler: javax.naming.directory.DirContext cn=Londo Mollari: javax.naming.directory.DirContext cn=Ted Geisel: javax.naming.directory.DirContext |
?
Context.listBindings()方法
Context.listBindings()方法返回綁定的枚舉。綁定是NameClassPair的子類。綁定不止包含對象名和對象類名,還包含對象。以下代碼片段枚舉了“ou=People”上下文,打印出每一個綁定名稱和對象。
?
NamingEnumeration bindings = ctx.listBindings("ou=People"); ? while (bindings.hasMore()) { ????Binding bd = (Binding)bindings.next(); ????System.out.println(bd.getName() + ": " + bd.getObject()); } |
?
返回的結(jié)果如下:
?
# java ListBindings cn=Jon Ruiz: com.sun.jndi.ldap.LdapCtx@1d4c61c cn=Scott Seligman: com.sun.jndi.ldap.LdapCtx@1a626f cn=Samuel Clemens: com.sun.jndi.ldap.LdapCtx@34a1fc cn=Rosanna Lee: com.sun.jndi.ldap.LdapCtx@176c74b cn=Maxine Erlund: com.sun.jndi.ldap.LdapCtx@11b9fb1 cn=Niels Bohr: com.sun.jndi.ldap.LdapCtx@913fe2 cn=Uri Geller: com.sun.jndi.ldap.LdapCtx@12558d6 cn=Colleen Sullivan: com.sun.jndi.ldap.LdapCtx@eb7859 cn=Vinnie Ryan: com.sun.jndi.ldap.LdapCtx@12a54f9 cn=Rod Serling: com.sun.jndi.ldap.LdapCtx@30e280 cn=Jonathan Wood: com.sun.jndi.ldap.LdapCtx@16672d6 cn=Aravindan Ranganathan: com.sun.jndi.ldap.LdapCtx@fd54d6 cn=Ian Anderson: com.sun.jndi.ldap.LdapCtx@1415de6 cn=Lao Tzu: com.sun.jndi.ldap.LdapCtx@7bd9f2 cn=Don Knuth: com.sun.jndi.ldap.LdapCtx@121cc40 cn=Roger Waters: com.sun.jndi.ldap.LdapCtx@443226 cn=Ben Dubin: com.sun.jndi.ldap.LdapCtx@1386000 cn=Spuds Mackenzie: com.sun.jndi.ldap.LdapCtx@26d4f1 cn=John Fowler: com.sun.jndi.ldap.LdapCtx@1662dc8 cn=Londo Mollari: com.sun.jndi.ldap.LdapCtx@147c5fc cn=Ted Geisel: com.sun.jndi.ldap.LdapCtx@3eca90 |
?
結(jié)束NamingEnumeration
NamingEnumeration可以通過三種方式終止:一般的,顯式的,非顯式的。
l?????????當(dāng)NamingEnumeration.hasMore()返回false,枚舉結(jié)束同時終止。
l?????????您可以在枚舉終止前請求NamingEnumeration.close()方法顯式的終止一個枚舉。這樣做提示底層實現(xiàn)釋放任何和枚舉有關(guān)的資源。
l?????????如果hasMore()或next()方法拋出任何異常,枚舉立即終止。
不管如何終止枚舉,枚舉一旦被終止就不能再使用。在一個已終止的枚舉中請求任何方法都會導(dǎo)致不確定的結(jié)果。
為什么使用兩個不同的方法?
list()為瀏覽類型的應(yīng)用程序準(zhǔn)備,只返回上下文中對象的名字。例如,瀏覽器可能列出上下文中的名字,期待用戶選擇一個或多個顯示的名稱來進(jìn)行后續(xù)操作。這些應(yīng)用程序一般不需要訪問上下文中所有的對象。
listBindings()為需要在上下文對象中進(jìn)行操作的應(yīng)用程序準(zhǔn)備。例如,備份程序需要在文件目錄中所有對象中執(zhí)行“file stats”操作?;蛘?#xff0c;一個打印機(jī)管理員程序可能想要重啟建筑物內(nèi)的所有打印機(jī)。為了執(zhí)行這些操作,需要得到上下文中的所有對象。因此,將對象作為枚舉的一部分返回是權(quán)宜之計。
應(yīng)用程序可以依據(jù)需要的信息類型選擇list()或listBindings()。
添加、替換、或刪除綁定
Context接口包含在上下文中添加、替換、刪除綁定的方法。
添加綁定
Context.bind()為了向上下文中添加綁定,它以對象類型以及需要綁定的對象作為參數(shù)。
?
在繼續(xù)前:本教程中的例子需要您對架構(gòu)做額外的修改。您必須關(guān)閉LDAP服務(wù)器的架構(gòu)檢測或?qū)⒎媳窘坛痰募軜?gòu)添加到服務(wù)器中。這種工作一般由目錄服務(wù)器管理員執(zhí)行。請看課程。
?
// Create the object to be bound Fruit fruit = new Fruit("orange"); ? // Perform the bind ctx.bind("cn=Favorite Fruit", fruit); |
?
這個例子創(chuàng)建了一個Fruit類的對象同時在上下文ctx中將他綁定到名稱“cn=Favorite Fruit”中。如果您隨后在ctx中查詢“cn=Favorite Fruit”,那么您將得到fruit兌現(xiàn)。注意,編譯Fruit類需要FruitFactory類。
如果您運(yùn)行這個例子兩次,那么第二次會因為NameAlreadyBoundException異常失敗。因為“cn=Favorite Fruit”已經(jīng)綁定了。要第二次運(yùn)行時不失敗,需要使用rebind()。
添加或修改綁定
rebind()用來添加或替換綁定。它的參數(shù)列表和bind()一樣,但如果名稱已經(jīng)綁定,那么首先會unbound然后再重新綁定新的對象。
?
// Create the object to be bound Fruit fruit = new Fruit("lemon"); ? // Perform the bind ctx.rebind("cn=Favorite Fruit", fruit); |
?
當(dāng)您運(yùn)行這個例子時,將會替換bind()例子中已經(jīng)創(chuàng)建的綁定關(guān)系。
刪除綁定
要刪除綁定,使用unbind()。
?
// Remove the binding ctx.unbind("cn=Favorite Fruit"); |
?
當(dāng)這個例子運(yùn)行時,刪除bind()或unbind()創(chuàng)建的綁定關(guān)系。
重命名
您使用Context.rename()對上下文中的對象進(jìn)行重命名。
?
// Rename to Scott S ctx.rename("cn=Scott Seligman", "cn=Scott S"); |
?
這個例子將綁定到“cn=Scott Seligman”的對象綁定到了“cn=Scott S”中。在驗證對象被重命名后,程序再將其重命名回原來的名字(“cn=Scott Seligman”),如下所示:
?
// Rename back to Scott Seligman ctx.rename("cn=Scott S", "cn=Scott Seligman"); |
?
更多關(guān)于LDAP中重命名的例子請參考LDAP的高級注意。
創(chuàng)建和銷毀子上下文
Context接口包含創(chuàng)建和銷毀一個子上下文。子上下文是綁定到其他上下文的上下文。
這個例子使用一個有屬性的對象,然后在目錄中創(chuàng)建子上下文。您可以使用DirContext的方法將屬性和對象在綁定或子上下文添加到名字空間時進(jìn)行關(guān)聯(lián)。例如,您可以創(chuàng)建Person對象,然后在為Person對象關(guān)聯(lián)屬性的同時將他綁定到命名空間中。命名等于沒有任何屬性。
createSubcontext()和bind()不同,他創(chuàng)建了一個新對象,例如一個要綁定到目錄中的新上下文,但bind()在目錄中綁定了給定的對象。
?
創(chuàng)建上下文
要創(chuàng)建命名上下文,您向createSubcontext()提供要創(chuàng)建上下文的名稱。要創(chuàng)建有屬性的上下文,向DirContext.createSubcontext()提供想要創(chuàng)建上下文的名稱以及需要的屬性。
?
在繼續(xù)前:本教程中的例子需要您對架構(gòu)做額外的修改。您必須關(guān)閉LDAP服務(wù)器的架構(gòu)檢測或?qū)⒎媳窘坛痰募軜?gòu)添加到服務(wù)器中。這種工作一般由目錄服務(wù)器管理員執(zhí)行。請看課程。
?
// Create attributes to be associated with the new context Attributes attrs = new BasicAttributes(true); // case-ignore Attribute objclass = new BasicAttribute("objectclass"); objclass.add("top"); objclass.add("organizationalUnit"); attrs.put(objclass); ? // Create the context Context result = ctx.createSubcontext("NewOu", attrs); |
?
這個例子創(chuàng)建了名稱為“ou=NewO”的上下文,并且有屬性“objectclass”,屬性“top”和“organizationalUnit”,其中“objectclass”有兩個值。
?
# java Create ou=Groups: javax.naming.directory.DirContext ou=People: javax.naming.directory.DirContext ou=NewOu: javax.naming.directory.DirContext |
?
這個例子創(chuàng)建了一個新的上下文,叫“NewOu”,是ctx的子上下文。
銷毀上下文
要銷毀上下文,需要向destroySubcontext()提供需要銷毀上下文的名稱。
?
// Destroy the context ctx.destroySubcontext("NewOu"); |
?
這個例子在上下文ctx中刪除上下文“NewOu”。
屬性名
屬性由屬性標(biāo)識符和一組屬性值組成。屬性表示符叫屬性名,是表示屬性的字符串。屬性值是屬性的內(nèi)容,它的類型不一定是字符串。當(dāng)您想要指定獲取、搜索、或修改指定屬性時,您使用屬性名。名稱同時在返回屬性的操作中返回(例如,當(dāng)您執(zhí)行目錄的讀取或搜索操作時)。
當(dāng)使用屬性名時,您需要知道特定目錄服務(wù)器的特性,所以您不會為結(jié)果驚奇。這些特定在下一子節(jié)中描述。
屬性類型
在諸如LDAP之類的目錄中,屬性名表示了屬性的類型,通常叫做屬性類型名。例如,屬性名“cn”同時叫做屬性類型名。屬性類型定義了屬性值的語法,是否允許多值,相等性,以及對屬性值執(zhí)行比較和排序時的排序規(guī)則,
屬性子類
一些目錄實現(xiàn)支持目錄子類型,就是服務(wù)器允許屬性類型使用其他屬性類型定義。例如,“name”屬性可能是所有name相關(guān)屬性的超類型:“commonName”是name的子類。對于支持這種特斯娜格的目錄實現(xiàn),訪問“name”屬性可能返回“commonName”屬性。
當(dāng)訪問支持子類型的屬性的目錄時,要知道服務(wù)器可能返回和您請求不一致的類型。為了減少這種幾率,使用最派生類。
屬性名同義詞
一些目錄實現(xiàn)支持屬性名的同義詞。例如,“cn”可能是“commonName”的同義詞。所以請求“cn”屬性可能返回“commonName”屬性。
當(dāng)訪問支持屬性同義詞的目錄,您必須意識到服務(wù)器可能返回和您請求不同的屬性名。要防止這種情況發(fā)生,使用官方的屬性名代替使用同義詞。官方的屬性名是定義屬性的屬性名;同義詞是是定義中引用官方屬性名的名稱。
語言參數(shù)選擇
LDAP v3的擴(kuò)展(RFC 2596)允許您和屬性名一起指定語言編碼。類似于子類屬性,一個屬性名可以表示多個不同的屬性。例如“description”屬性有兩個不同的語言變體:
?
description: software description;lang-en: software products description;lang-de: Softwareprodukte |
?
對“description”屬性的請求會返回所有三種屬性。當(dāng)訪問支持這種特性的目錄時,您必須意識到服務(wù)器可能返回和請求時不同的名稱。
讀取屬性
為了從目錄中讀取對象的屬性,使用DirContext.getAttributes()并且將您想讀取的屬性名稱傳遞進(jìn)去就可以了。假設(shè)在命名服務(wù)中的一個對象的名稱是“cn=Ted Geisel, ou=People”。要獲取對象的屬性要使用如下代碼:
?
Attributes answer = ctx.getAttributes("cn=Ted Geisel, ou=People"); |
?
您可以按照如下方式打印應(yīng)答的內(nèi)容:
?
for (NamingEnumeration ae = answer.getAll(); ae.hasMore();) { ????Attribute attr = (Attribute)ae.next(); ????System.out.println("attribute: " + attr.getID()); ????/* Print each value */ ????for (NamingEnumeration e = attr.getAll(); e.hasMore(); ?????System.out.println("value: " + e.next())) ????; } |
?
輸出如下:
?
# 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 |
?
返回選中屬性
為了讀取選中子集的屬性,您需要提供想要獲取的屬性標(biāo)識符的數(shù)組。
?
// Specify the ids of the attributes to return String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"}; ? // Get the attributes requested Attributes answer = ctx.getAttributes("cn=Ted Geisel, ou=People", attrIDs); |
?
這個例子請求對象“cn=Ted Geisel, ou=People”的“sn”,“telephonenumber”,“golfhandicap”和“mail”屬性,所以應(yīng)答中返回這三個屬性。
以下是輸出結(jié)果:
?
# java Getattrs attribute: sn value: Geisel attribute: mail value: Ted.Geisel@JNDITutorial.com attribute: telephonenumber value: +1 408 555 5252 |
?
修改屬性
DirContext接口包含修改目錄中對象的屬性和屬性值的方法。
使用修改列表
修改對象屬性的一個方法是提供修改列表(ModificationItem)。每一個ModificationItem包含數(shù)字常量表示修改的類型以及描述需要修改的屬性。以下是修改的類型。
l?????????ADD_ATTRIBUTE
l?????????REPLACE_ATTRIBUTE
l?????????REMOVE_ATTRIBUTE
修改以列表中提供的類型進(jìn)行?;蛘咚械男薷亩紙?zhí)行,或者一個都不執(zhí)行。
以下代碼創(chuàng)建修改列表。它將“mail”屬性值替換成geisel@wizards.com,為“telephonenumber”屬性添加附加值,并且刪除“jpegphoto”屬性。
?
// Specify the changes to make ModificationItem[] mods = new ModificationItem[3]; ? // Replace the "mail" attribute with a new value mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, ????new BasicAttribute("mail", "geisel@wizards.com")); ? // Add an additional value to "telephonenumber" mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, ????new BasicAttribute("telephonenumber", "+1 555 555 5555")); ? // Remove the "jpegphoto" attribute mods[2] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, ????new BasicAttribute("jpegphoto")); |
?
Windows活動目錄:活動目錄將“telephonenumber”屬性定義為單值屬性,這和RFC 2256不符。為了讓這個例子在活動目錄中執(zhí)行,您必須或者使用其他屬性代替“telephonenumber”或?qū)irContext.ADD_ATTRIBUTE改為DirContext.REPLACE_ATTRIBUTE。
?
在創(chuàng)建修改列表后,您可以按照如下方式提供給modifyAttributes():
?
// Perform the requested modifications on the named object ctx.modifyAttributes(name, mods); |
?
使用屬性
可選的,您可以通過指定修改類型以及修改屬性的方式進(jìn)行修改。
例如,以下行使用orig中的name關(guān)聯(lián)需要替換的屬性:
?
ctx.modifyAttributes(name, DirContext.REPLACE_ATTRIBUTE, orig); |
?
其他屬性的名稱沒有改變。
兩種對于modifyAttributes()的使用在示例程序中都有。使用修改列表修改屬性的程序在第二部分modifyAttributes()中恢復(fù)了原來的屬性。
有屬性的添加、刪除綁定
命名的例子討論如何使用bind()和unbind(),DirContext這兩方法的重載版本。您使用DirContext的方法關(guān)聯(lián)對象的屬性,在綁定或子上下文時將她們添加到名字空間中。例如,您可能創(chuàng)建了Person對象,然后在為Person對象關(guān)聯(lián)屬性時將他綁定到名字空間中。
添加有屬性的綁定
DirContext.bind()用來將屬性綁定添加到上下文中。它的參數(shù)為需要綁定的對象名稱, 以及屬性集合。
?
// Create the object to be bound Fruit fruit = new Fruit("orange"); ? // Create attributes to be associated with the 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.bind("ou=favorite, ou=Fruits", fruit, attrs); |
?
這個例子創(chuàng)建了Fruit類的對象并且將他綁定到“ou=Fruits”上下文中,名稱為“ou=favorite”。綁定有“objectclass”屬性。如果接下來在ctx中查詢“ou=favorite, ou=Fruits”,那么您將得到fruit對象。如果您想、得到“ou=favorite, ou=Fruits”的屬性,您將得到剛才為對象添加的屬性。以下是例子的輸出:
?
# java Bind orange attribute: objectclass value: top value: organizationalUnit value: javaObject value: javaNamingReference attribute: javaclassname value: Fruit attribute: javafactory value: FruitFactory attribute: javareferenceaddress value: #0#fruit#orange attribute: ou value: favorite |
?
顯示的多于屬性使用來保存關(guān)于對象(fruit)的一些信息。這些多于信息隨后會進(jìn)行詳細(xì)介紹。
如果您兩次運(yùn)行這個例子,那么第二次運(yùn)行時將會失敗并拋出NameAlreadyBoundException。這是因為“ou=favorite”已經(jīng)綁定到上下文“ou=Fruits”中。如果要成功,需要使用rebind()。
替換有屬性的綁定
DirContext.rebind()的作用是添加或修改綁定以及屬性。它接受和bind()一樣的參數(shù)。然而,使用rebind()時如果名稱已經(jīng)存在,那么將會首先Unbind然后再綁定新的對象和屬性。
?
// Create the object to be bound Fruit fruit = new Fruit("lemon"); ? // Create attributes to be associated with the 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); |
?
運(yùn)行這個例子時,它替換了bind()例子中創(chuàng)建的綁定關(guān)系。
?
# java Rebind lemon attribute: objectclass value: top value: organizationalUnit value: javaObject value: javaNamingReference attribute: javaclassname value: Fruit attribute: javafactory value: FruitFactory attribute: javareferenceaddress value: #0#fruit#lemon attribute: ou value: favorite |
?
搜索
目錄提供的最有用的好處就是黃頁功能,或搜索服務(wù)。您可以組合一個包含條目屬性的查詢,然后提交查詢到目錄中。然后目錄返回滿足查詢的條目列表。例如,您可以訪問目錄,查詢出保齡球平均成績大于200的條目,或所有姓以“Sch”開頭的人。
DirContext接口提供查詢條目的方法,這些方法很復(fù)雜也很強(qiáng)大。搜索目錄的不同方面在以下章節(jié)中描述:
l?????????基本搜索
l?????????搜索過濾器
l?????????搜索控制
基本搜索
最簡單的查詢需需要您指定條目必須含有的屬性集合以及進(jìn)行查詢的目標(biāo)上下文。
以下代碼創(chuàng)建了屬性集合matchAttrs,其中有兩個屬性“sn”和“mail”。表名搜索條目必須有姓(sn)屬性,其值為“Geisel”,以及“mail”屬性可以是任意值。然后調(diào)用DirContext.search()查詢上下文“ou=People”中和matchAttrs匹配的條目。
?
// Specify the attributes to match // Ask for objects that has a surname ("sn") attribute with // the value "Geisel" and the "mail" attribute Attributes matchAttrs = new BasicAttributes(true); // ignore attribute name case matchAttrs.put(new BasicAttribute("sn", "Geisel")); matchAttrs.put(new BasicAttribute("mail")); ? // Search for objects that have those matching attributes NamingEnumeration answer = ctx.search("ou=People", matchAttrs); ? You can then print the results as follows. while (answer.hasMore()) { ????SearchResult sr = (SearchResult)answer.next(); ????System.out.println(">>>" + sr.getName()); ????printAttrs(sr.getAttributes()); } |
?
printAttrs()方法和getAttributes()方法打印出屬性集合類似。
返回結(jié)果如下:
?
# 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 |
?
返回選中屬性
上一個例子返回滿足指定查詢條件條目的所有屬性。您可以通過向search()傳遞屬性標(biāo)識符數(shù)組的方式選擇想要包含在結(jié)果集中的屬性。在創(chuàng)建matchAttrs只有,您應(yīng)該創(chuàng)建屬性標(biāo)識符的數(shù)組,如下所示:
?
// Specify the ids of the attributes to return String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"}; ? // Search for objects that have those matching attributes NamingEnumeration answer = ctx.search("ou=People", matchAttrs, attrIDs); |
?
這個例子返回條目的屬性“sn”,“telephonenumber”,“golfhandicap”以及“mail”,其中條目有屬性“mail”并且“sn”屬性的值是“Geisel”。這個例子產(chǎn)生如下結(jié)果。(條目中沒有“golfhandicap”屬性,所以沒有返回)。
?
# java Search >>>cn=Ted Geisel attribute: sn value: Geisel attribute: mail value: Ted.Geisel@JNDITutorial.com attribute: telephonenumber value: +1 408 555 5252 |
?
過濾器
除了使用指定屬性集合進(jìn)行搜索外,您可以用搜索過濾器的形式進(jìn)行搜索。搜索過濾器是一種搜索用的邏輯表達(dá)式。DirContext.search()可接受的過濾器語法在RFC 2254中定義。
以下搜索過濾器指定了滿足查詢條件的條目必須有值為“Geisel”的“sn”屬性,以及值為任意的“mail”屬性:
?
(&(sn=Geisel)(mail=*)) |
?
以下代碼創(chuàng)建了過濾器以及默認(rèn)的SearchControls,使用他們執(zhí)行查詢。這個查詢的結(jié)果和基本查詢中的一致。
?
// Create the default search controls SearchControls ctls = new SearchControls(); ? // Specify the search filter to match // Ask for objects that have the attribute "sn" == "Geisel" // and the "mail" attribute String filter = "(&(sn=Geisel)(mail=*))"; ? // Search for objects using the filter NamingEnumeration answer = ctx.search("ou=People", filter, ctls); |
?
搜索結(jié)果如下:
?
# java SearchWithFilterRetAll >>>cn=Ted Geisel attribute: sn value: Geisel attribute: objectclass value: top value: person value: organizationalPerson value: inetOrgPerson attribute: jpegphoto value: [B@1dacd75e 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 |
?
搜索過濾器語法概述
搜索過濾器是前綴標(biāo)記的搜索表達(dá)式(邏輯運(yùn)算符在表達(dá)式前面)。下表列舉了創(chuàng)建過濾器使用的符號。
?
符號 | 描述 |
& | 與(列表中所有項必須為true) |
| | 或(列表中至少一個必須為true) |
! | 非(求反的項不能為true) |
= | 相等(根據(jù)屬性的匹配規(guī)則) |
~= | 近似等于(根據(jù)屬性的匹配規(guī)則) |
>= | 大于(根據(jù)屬性的匹配規(guī)則) |
<= | 小于(根據(jù)屬性的匹配規(guī)則) |
=* | 存在(條目中必須有這個屬性,但值不做限制) |
* | 通配符(表示這個位置可以有一個或多個字符),當(dāng)指定屬性值時用到 |
\ | 轉(zhuǎn)義符(當(dāng)遇到“*”,“(”,“)”時進(jìn)行轉(zhuǎn)義) |
?
過濾器中的每一個條目使用屬性標(biāo)識符和屬性值或符號表示屬性值組成。例如,項“sn=Geisel”表示“sn”屬性的值必須為“Geisel”,同時項“mail=*”表示“mail”屬性必須存在。
每項必須包含在括號之內(nèi),例如“(sn=Geisel)”。這些項使用邏輯運(yùn)算符,例如&,創(chuàng)建邏輯表達(dá)式,例如“(& (sn=Geisel) (mail=*))”。
每一個邏輯表達(dá)式可以進(jìn)一步組成其他項,例如“(| (& (sn=Geisel) (mail=*)) (sn=L*))”。這個例子請求的條目中或者含有值為“Geisel”的“sn”屬性和“mail”屬性或者“sn”屬性以字母“L”開頭。
關(guān)于語法的詳細(xì)描述,請參考RFC 2254。
返回選中屬性
上一個例子返回滿足指定過濾器的條目中的所有屬性。您可以通過設(shè)置搜索控制參數(shù)的方法選擇返回屬性。您創(chuàng)建想要包含在結(jié)果中的屬性標(biāo)識符集合,然后將他傳遞到SearchControls.setReturningAttributes()中。如下所示:
?
// Specify the ids of the attributes to return String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"}; SearchControls ctls = new SearchControls(); ctls.setReturningAttributes(attrIDs); |
?
這個例子和基本搜索一節(jié)中返回選擇的屬性部分的結(jié)果一致。運(yùn)行后的結(jié)果如下。(這個條目沒有“golfhandicap”屬性,所以沒有返回)。
?
# java SearchWithFilter >>>cn=Ted Geisel attribute: sn value: Geisel attribute: mail value: Ted.Geisel@JNDITutorial.com attribute: telephonenumber value: +1 408 555 5252 |
?
范圍
默認(rèn)的SearchControls指定搜索只在命名空間中進(jìn)行(SearchControls.ONELEVEL_SCOPE)。這個默認(rèn)選項在搜索過濾器一節(jié)中使用。
除了默認(rèn)選項之外,您可以指定搜索在整個子樹或只在命名對象中執(zhí)行。
搜索子樹
對于整個子樹的搜索不但搜索命名對象而且搜索它的后代。要按照這種方式進(jìn)行搜索,按照下面的方式向SearchControls.setSearchScope()中傳遞SearchControls.SUBTREE_SCOPE參數(shù):
?
// Specify the ids of the attributes to return String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"}; SearchControls ctls = new SearchControls(); ctls.setReturningAttributes(attrIDs); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ? // Specify the search filter to match // Ask for objects that have the attribute "sn" == "Geisel" // and the "mail" attribute String filter = "(&(sn=Geisel)(mail=*))"; ? // Search the subtree for objects by using the filter NamingEnumeration answer = ctx.search("", filter, ctls); |
?
這個例子搜索了ctx上下文的子樹,得到滿足搜索過濾器的條目。它在子樹中找到了滿足過濾器的“cn= Ted Geisel, ou=People”條目。
?
# java SearchSubtree >>>cn=Ted Geisel, ou=People attribute: sn value: Geisel attribute: mail value: Ted.Geisel@JNDITutorial.com attribute: telephonenumber value: +1 408 555 5252 |
?
搜索命名對象
您也可以搜索命名對象。這樣做是很有用的,例如,測試命名對象是否滿足搜索過濾器。為了搜索命名對象,將SearchControls.OBJECT_SCOPE傳遞到setSearchScope()中。
?
// Specify the ids of the attributes to return String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"}; SearchControls ctls = new SearchControls(); ctls.setReturningAttributes(attrIDs); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); ? // Specify the search filter to match // Ask for objects that have the attribute "sn" == "Geisel" // and the "mail" attribute String filter = "(&(sn=Geisel)(mail=*))"; ? // Search the subtree for objects by using the filter NamingEnumeration answer = ????ctx.search("cn=Ted Geisel, ou=People", filter, ctls); |
?
這個例子測試是否對象“cn=Ted Geisel, ou=People”滿足給定的過濾器。
?
# java SearchObject >>>? attribute: sn value: Geisel attribute: mail value: Ted.Geisel@JNDITutorial.com attribute: telephonenumber value: +1 408 555 5252 |
?
例子找到了一個結(jié)果并進(jìn)行打印。注意結(jié)果中的名稱字段為空。因為對象的名稱是進(jìn)行查詢的上下文(cn=Ted Geisel, ou=People)。
結(jié)果數(shù)量
有時,查詢可能返回了太多的結(jié)果同時您想限制結(jié)果集的大小。這時您可以使用限制數(shù)量的搜索控制。默認(rèn)情況下,搜索沒有數(shù)量限制――它將會返回找到的所有結(jié)果。要設(shè)置搜索的數(shù)量限制,將數(shù)字傳遞到SearchControls.setCountLimit()中。
以下例子設(shè)置數(shù)量限制為1。
?
// Set the search controls to limit the count to 1 SearchControls ctls = new SearchControls(); ctls.setCountLimit(1); |
?
如果程序嘗試得到更多的結(jié)果,那么會拋出SizeLimitExceededException。如果程序設(shè)置數(shù)量限制,那么或者將這個異常和NamingExceptions異常區(qū)別對待或者按照數(shù)量限制的大小,不請求超過數(shù)量的結(jié)果。
滿足搜索數(shù)量限制是控制程序消耗資源的一種方法(例如內(nèi)存和網(wǎng)絡(luò)帶寬)。其他控制資源的方法是盡量使用小的搜索過濾器,在合適的上下文中開始搜索,使用合適的范圍。
時間限制
搜索時間限制是搜索操作等待應(yīng)答的時間上限。當(dāng)您不想為應(yīng)答等待太長時間時這很有用。如果搜索操作超過時間限制還沒完成,那么將會拋出TimeLimitExceededException異常。
為了設(shè)置搜索的時間限制,將毫秒數(shù)傳遞到SearchControls.setTimeLimit()即可。以下例子將時間限制設(shè)置為1秒。
?
// Set the search controls to limit the time to 1 second (1000 ms) SearchControls ctls = new SearchControls(); ctls.setTimeLimit(1000); |
?
要讓這個例子運(yùn)行超時,需要重新配置,或者使用一個慢的服務(wù)器或這使用有很多條目的服務(wù)器。您還可以使用其他方式讓搜索超過1秒。
時間限制為0表示不進(jìn)行時間限制,這樣請求將會進(jìn)行無限等待。
常見問題解答
當(dāng)您使用JNDI類運(yùn)行成功編譯的程序時可能遇到的主要問題如下:
?
l?????????沒有初始化上下文
l?????????連接被拒絕
l?????????連接失敗
l?????????程序掛起
l?????????名字沒有找到
l?????????不能連接任意主機(jī)
l?????????不能配置訪問系統(tǒng)屬性
l?????????不能使用CRAM-MD5認(rèn)證
?
1.????得到NoInitialContextException
原因:您沒有指定使用的初始化上下文。特別的,Context.INITIAL_CONTEXT_FACTORY環(huán)境變量沒有設(shè)置成為初始化上下文的工廠類名。后者,找不到Context.INITIAL_CONTEXT_FACTORY配置的可得到的服務(wù)提供者。
解決方案:將環(huán)境變量Context.INITIAL_CONTEXT_FACTORY設(shè)置為您使用的初始化上下文類名。詳細(xì)信息請參考配置一節(jié)。
如果屬性已經(jīng)設(shè)置,那么確認(rèn)類名沒有輸入錯誤,并且類在您的程序中可見(或者在classpath中或者在JRE的jre/lib/ext目錄下)。Java平臺包含服務(wù)提供者有LDAP,COS命名,DNS以及RMI注冊。所有其他的服務(wù)提供者必須安裝并且添加到執(zhí)行環(huán)境中。
2.????得到CommunicationException異常,表示“連接被拒絕”。
原因:Context.PROVIDER_URL環(huán)境參數(shù)表示的服務(wù)器和端口沒有提供訪問。可能有人禁用或關(guān)閉了服務(wù)?;蛘咻斎肓隋e誤的服務(wù)器名稱或端口號。
解決方案:檢查端口上確實運(yùn)行了服務(wù),如果需要就重啟服務(wù)器。這種檢查依賴于您使用的LDAP服務(wù)器。通常,可以使用管理控制臺或工具管理服務(wù)器。您可以使用工具確認(rèn)服務(wù)器的狀態(tài)。
3.????LDAP服務(wù)器向其他工具應(yīng)答(例如管理控制臺)但不應(yīng)答您程序的請求。
原因:服務(wù)器沒有應(yīng)答LDAP v3的連接全逆光球。一些服務(wù)器(尤其是公共服務(wù)器)不能正確的應(yīng)答LDAP v3,使用忽略的方式代替拒絕。同時,一些LDAP v3服務(wù)器有錯誤處理機(jī)制,Sun的LDAP服務(wù)提供者自動發(fā)送并且通常返回特定服務(wù)器錯誤碼。
解決方案:嘗試設(shè)置環(huán)境參數(shù)“java.naming.ldap.version”為“2”。LDAP服務(wù)提供者默認(rèn)嘗試使用LDAP v3連接LDAP服務(wù)器,然后使用LDAP v2。如果服務(wù)器靜默忽略v3的請求,那么提供者假設(shè)請求生效了。使用這種服務(wù)器,您必須顯式的設(shè)置協(xié)議版本,確保服務(wù)器有正確的行為。
如果服務(wù)器是v3服務(wù)器,那么嘗試在創(chuàng)建初始化上下文之前設(shè)置這些環(huán)境參數(shù):
?
env.put(Context.REFERRAL, "throw"); |
?
這樣關(guān)閉了LDAP提供者自動發(fā)送的控制(更多信息請參考JNDI教程)。
4.????程序掛起。
原因:當(dāng)您嘗試執(zhí)行的查實會生成太多結(jié)果或需要服務(wù)器查詢很多條目才能生成結(jié)果時,服務(wù)器(尤其是公共的)不應(yīng)答(不是一個失敗應(yīng)答)。這種服務(wù)器基于預(yù)請求計算花費(fèi)的方式嘗試減少資源消耗。
或者,您嘗試使用安全套接字層(SSL)但服務(wù)器/端口不支持,反之(您嘗試使用普通套接字與SSL端口對話)。
最終,服務(wù)器或者因為負(fù)載原因非常慢的應(yīng)答,或完全不應(yīng)答某些請求。
解決方案:如果程序因為服務(wù)器為了減少資源消耗而掛起,那么重試請求會得到單一應(yīng)答或只有很少的應(yīng)答。這樣可以幫助您判斷服務(wù)器是否還在活動。這樣,您可以加寬原有查詢,重新發(fā)送。
如果您的程序因為SSL問題掛起,那么您需要找到SSL端口然后正確設(shè)置Context.SECURITY_PROTOCOL環(huán)境參數(shù)。如果端口是SSL端口,那么這個參數(shù)應(yīng)該設(shè)置成“ssl”。如果不是SSL端口,這個參數(shù)不應(yīng)該設(shè)置。
如果程序不是因為上述原因掛起,使用com.sun.jndi.ldap.read.timeout表示讀取超時。這個參數(shù)的值是一個字符串,表示LDAP請求讀取超時的毫秒數(shù)。如果LDAP提供者不能在周期內(nèi)得到應(yīng)答,那么放棄讀取嘗試。數(shù)字應(yīng)該大于0。等于或小于0表示不指定讀取超時時間,等于無限等待得到應(yīng)答。
如果沒有指定這個參數(shù),默認(rèn)情況下會一直等待,直到得到應(yīng)答。
例如:
?
env.put("com.sun.jndi.ldap.read.timeout", "5000"); |
?
表示如果LDAP服務(wù)器不能在5秒中內(nèi)應(yīng)答,將放棄讀取請求。
5.????您得到NameNotFoundException異常。
原因:當(dāng)您為LDAP初始化了初始上下文,提供了根辨別名。例如,如果您為初始上下文設(shè)置Context.PROVIDER_URL為“l(fā)dap://ldapserver:389/o=JNDITutorial”,然后提供名稱“cn=Joe,c=us”,那么您向LDAP服務(wù)傳遞的全名為“cn=Joe,c=us,o=JNDITutorial”。如果這確實是您想要的名稱,那么您應(yīng)該檢驗服務(wù)器確定包含這個條目。
同時,如果您在認(rèn)證時提供錯誤的辨別名,Sun Java目錄服務(wù)器返回錯誤。例如,如果您設(shè)置Context.SECURITY_PRINCIPAL?環(huán)境參數(shù)為“cn=Admin, o=Tutorial”,并且“cn=Admin, o=Tutorial”不是LDAP服務(wù)器的條目,LDAP提供者將要拋出NameNotFoundException。Sun Java目錄服務(wù)器返回的實際上是一些認(rèn)證異常,不是“name not found”。
解決方案:確認(rèn)您提供的名字是服務(wù)器上存在的。您可以通過列舉父上下文的所有條目或使用其他工具例如服務(wù)器的管理員控制臺來確認(rèn)。
?
以下是在部署applet時可能遇到的問題。
?
6.????當(dāng)您的applet嘗試連接目錄服務(wù)器時得到AppletSecurityException異常,服務(wù)器正運(yùn)行在和下載applet不同的機(jī)器上。
原因:applet沒有簽名,所以只能連接到加載它的機(jī)器。或者,如果appet已經(jīng)簽名,瀏覽器沒有授予applet連接目錄服務(wù)器的權(quán)限。
解決方案:如果您想允許applet連接任意機(jī)器上的目錄服務(wù)器,那么需要簽名整個applet以及applet使用的JNDI jar。關(guān)于jar的簽名,請參考簽名和驗證jar文件。
7.????當(dāng)您的applet嘗試使用系統(tǒng)屬性設(shè)置環(huán)境屬性時出現(xiàn)AppletSecurityException異常。
原因:瀏覽器限制訪問系統(tǒng)參數(shù),并且當(dāng)您嘗試讀取時拋出SecurityException。
解決方案:如果您需要為applet得到輸入,嘗試使用applet params代替。
8.????當(dāng)applet運(yùn)行在Firefox中嘗試使用CRAM-MD5向LDAP進(jìn)行認(rèn)證時拋出AppletSecurityException。
原因:Firefox禁止訪問java.security包。LDAP提供者使用java.security.MessageDigest提供的信息摘要功能來實現(xiàn)CRAM-MD5。
解決方案:使用Java插件。
?
轉(zhuǎn)自:https://blog.csdn.net/liumm0000/article/details/7932019