6.2 使用JNDI?
6.2.1? JNDI服務(wù)提供者
不使用服務(wù)提供者就不能用JNDI。一個(gè)服務(wù)提供者就是一組Java類的集合,它支持開(kāi)發(fā)者同目錄服務(wù)進(jìn)行通信,其方式類似于JDBC驅(qū)動(dòng)程序與數(shù)據(jù)庫(kù)之間的通信方式。能夠用于JNDI的服務(wù)提供者必須實(shí)現(xiàn)Context接口或Context的擴(kuò)展接口Directory- Context。
在使用JNDI時(shí),讀者只需要了解JNDI,而服務(wù)提供者才關(guān)注實(shí)際的網(wǎng)絡(luò)協(xié)議、編碼/解碼值等細(xì)節(jié)。
當(dāng)下載SDK軟件開(kāi)發(fā)包時(shí),同時(shí)就下載了Sun公司的一些現(xiàn)有的服務(wù)提供者。這些服務(wù)提供者包括LDAP、NIS、COS(CORBA對(duì)象服務(wù))、RMI注冊(cè)及文件系統(tǒng)的提供者。如:hashtableObj.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.ldapCtx- Fatory")就是表示使用Sun LDAP服務(wù)提供者。當(dāng)然如果要使用IBM服務(wù)提供者時(shí)就可以用com.ibm.jndi.LDAPCtxFatory來(lái)代替com.sun.jndi.ldap.ldapCtxFatory。
6.2.2? JNDI的包
JNDI中包括5個(gè)包。
·?????? javax.naming:主要用于命名操作,它包含了命名服務(wù)的類和接口,該包定義了Context接口和InitialContext類;
·?????? javax.naming.directory:主要用于目錄操作,它定義了DirContext接口和InitialDir- Context類;
·?????? javax.naming.event:在命名目錄服務(wù)器中請(qǐng)求事件通知;
·?????? javax.naming.ldap:提供LDAP支持;
·?????? javax.naming.spi:允許動(dòng)態(tài)插入不同實(shí)現(xiàn),為不同命名目錄服務(wù)供應(yīng)商的開(kāi)發(fā)人員提供開(kāi)發(fā)和實(shí)現(xiàn)的途徑,以便應(yīng)用程序通過(guò)JNDI可以訪問(wèn)相關(guān)服務(wù)。
6.2.3? 常用的JNDI操作
常用的JNDI操作如下:
·?????? void bind(String sName,Object object),綁定:把名稱同對(duì)象關(guān)聯(lián)的過(guò)程。
·?????? void rebind(String sName,Object object),重新綁定:用來(lái)把對(duì)象同一個(gè)已經(jīng)存在的名稱重新綁定。一般使用rebind()而不使用bind(),因?yàn)楫?dāng)有重名的時(shí)候rebind()不會(huì)出現(xiàn)異常,而bind()會(huì)報(bào)異常。
·?????? void unbind(String sName),釋放:用來(lái)把對(duì)象從目錄中釋放出來(lái)。
·?????? void lookup(String sName,Object object),查找:返回目錄總的一個(gè)對(duì)象。
·?????? void rename(String sOldName,String sNewName),重命名:用來(lái)修改對(duì)象名稱綁定的名稱。
·?????? NamingEnumeration listBindings(String sName),清單:返回綁定在特定上下文中指定屬性名對(duì)象的清單列表,它返回名字、類和對(duì)象本身,它用于那些需要對(duì)對(duì)象進(jìn)行實(shí)際操作的應(yīng)用。具體使用如下:
java 代碼
?
?? ?? Context?cntxt?=?new?InitialContext(); ?? ?? ?? ?? NamingEnumeration?namEnumList?=?ctxt.listBinding("cntxtName"); ?? ?? ?? ?? while?(?namEnumList.hasMore()?)??{ ?? ?? ????Binding?bnd?=?(Binding)?namEnumList.next(); ?? ?? ????String?sObjName?=?bnd.getName(); ?? ?? ????String?sClassName?=?bnd.getClassName(); ?? ?? ?????? ?? ????SomeObject?objLocal?=?(SomeObject)?bnd.getObject(); ?? ?? } ??
·?????? NamingEnumeration list(String sName)與listBindings(String sName)相似,只是它只返回一系列名字/類映射,它主要是用于上下文瀏覽應(yīng)用。
6.2.4? JNDI操作步驟
使用JNDI來(lái)訪問(wèn)命名服務(wù)或者目錄服務(wù),操作步驟如下:
(1)建立一個(gè)散列表(hashtable),它包含定義所希望使用的JNDI服務(wù)的屬性,所希望連接的LDAP服務(wù)器IP地址以及工作的端口。
(2)將與認(rèn)證成為用戶登錄有關(guān)的任何信息添加到散列表中。
(3)創(chuàng)建初始context對(duì)象。如果訪問(wèn)命名服務(wù),則使用InitialContext類,如果訪問(wèn)目錄服務(wù),則要使用InitialDirContext類。
(4)使用剛才得到的context對(duì)象執(zhí)行所需的操作(如添加新的條目或者搜索條目)。
(5)完成操作后關(guān)閉context對(duì)象。
6.2.5? JNDI允許存儲(chǔ)的對(duì)象類型
JNDI最大的功能是能使用LDAP來(lái)存儲(chǔ)需要在不同應(yīng)用之間共享或者留做備用的對(duì)象。JNDI允許將下面幾種與Java相關(guān)的對(duì)象類型存儲(chǔ)到LDAP服務(wù)器內(nèi)。
(1)串行化的Java對(duì)象。這是存儲(chǔ)和取回已經(jīng)串行化的Java對(duì)象的能力。也就是??? 說(shuō)要存儲(chǔ)的Java對(duì)象必須要實(shí)現(xiàn)Referenceable或Serializable接口類,否則該對(duì)象不能存儲(chǔ)。
(2)標(biāo)準(zhǔn)的LDAP目錄條目。它提供了操作標(biāo)準(zhǔn)目錄數(shù)據(jù)的能力。標(biāo)準(zhǔn)目錄數(shù)據(jù)的數(shù)據(jù)量比較小,可以在不同的語(yǔ)言之間共享它們。保持目錄數(shù)據(jù)與編程語(yǔ)言的無(wú)關(guān)性對(duì)于要使用幾種不同語(yǔ)言進(jìn)行開(kāi)發(fā)的大企業(yè)里是非常重要的。
(3)指向RMI Java對(duì)象的指針。RMI是用于分布式計(jì)算的,通過(guò)RMI,一個(gè)Java應(yīng)用可以像本地一樣調(diào)用一個(gè)遠(yuǎn)程類的方法。我們可以把一個(gè)可用的RMI類的引用存儲(chǔ)在開(kāi)發(fā)者的LDAP服務(wù)器中,而不必在每個(gè)裝有RMI客戶應(yīng)用的計(jì)算機(jī)上都保持可用方法的注冊(cè)。
6.2.6? JNDI存儲(chǔ)查詢串行化的Java對(duì)象
JNDI的主要目標(biāo)是在網(wǎng)絡(luò)上讀/寫Java對(duì)象。下面用具體實(shí)例來(lái)了解怎么使用JNDI。首先通過(guò)一個(gè)例子來(lái)講解怎么樣在LDAP中保存串行化的Java對(duì)象數(shù)據(jù),再用一個(gè)例子來(lái)說(shuō)明怎么對(duì)保存的對(duì)象數(shù)據(jù)進(jìn)行查詢、調(diào)用。
1.保存數(shù)據(jù)
在LDAP中保存數(shù)據(jù)就是在LDAP服務(wù)器中添加使用條目,也就是把條目綁定在服務(wù)器中。下面先建立一個(gè)基本類,再在另一個(gè)類中利用JNDI把這個(gè)基本類綁定在服務(wù)器中。
4 例6-1 ?在LDAP中保存數(shù)據(jù)。
(1)待綁定的基本類
java 代碼
package?jndi; ?? ?? import?java.io.serializable; ?? ?? public?class?persons??implements?Serializable?{ ?? ?? ????String?Name?=?""; ?? ?? ????String?Age?=""??; ?? ?? ????public?persons?()?{ ?? ?? ????} ?? ?? ?????? ?? ????public?persons?(String?namePara,String?age)?{ ?? ?? ????????Name?=?namePara; ?? ?? ????????Age?=?age; ?? ?? ????} ?? ?? ?????? ?? ????public?String?getName()?{ ?? ?? ????????return?Name; ?? ?? ????} ?? ?? ?????? ?? ????public?String?getAge?()?{ ?? ?? ????return??Age; ?? ?? ????} ?? ?? } ?? ??
JNDI定義了一個(gè)Serializable接口類來(lái)為應(yīng)用信息的表達(dá)提供一種統(tǒng)一的方式。Serializable接口類包含了諸如地址、類型信息等用于訪問(wèn)具體對(duì)象的信息。為了能將對(duì)象的引用綁定到目錄樹(shù)中,該對(duì)象的類必須實(shí)現(xiàn)Referenceable接口,其中包含了方法 getReference()。開(kāi)發(fā)者可以在該對(duì)象上調(diào)用getReference()方法來(lái)獲得Reference以用于綁定。Serializable接口與Referenceable接口有頗多相似之處,不同在于Referenceable可引用的對(duì)象只包含一些用于創(chuàng)建實(shí)際對(duì)象的信息,而Serializable會(huì)包含更多的甚至不適合存儲(chǔ)在目錄結(jié)構(gòu)中的信息。
(2)綁定保存對(duì)象程序
java 代碼
package?jndi; ?? ?? import?java.util.Hashtable; ?? ?? import?javax.naming.Context; ?? ?? import?javax.naming.NamingException;???? ?? ?? import?javax.naming.directory.*; ?? ?? public?class?ldapDataBind?{ ?? ?? ???public?static?void?main(String[]args){ ?? ?? ????????? ?? ????????Hashtable?hs?=?new?Hashtable(); ?? ?? ?????????? ?? ????????hs.put(Context.INITIAL_CONTEXT_FACTORY,? ?? ?? ??????????????????????"com.sun.jndi.ldap.LdapCtxFactory"); ?? ?? ?????????? ?? ????????hs.put(Context.PROVIDER_URL,?"ldap://localhost:389?"); ?? ?? ?????????? ?? ????????hs.put(Context.SECURITY_AUTHENTICATION,?"simple"); ?? ?? ?????????? ?? ????????hs.put(Context.SECURITY_PRINCIPAL,?"cn=Directory?Manager"); ?? ?? ?????????? ?? ????????hs.put(Context.SECURITY_CREDENTIALS,?"password"); ?? ?? ????????try?{ ?? ?? ????????????? ?? ???????????DirContext?ctx?=?new?InitialDirContext(hs); ?? ?? ????????????? ?? ???????????persons?perObj?=?new?persons("jordan","40"); ?? ?? ????????????? ?? ???????????ctx.rebind?("uid?=?Jordan,ou?=?Bull,o?=?NBA?",perObj); ?? ?? ???????????System.out.println("bind?object?object?success?"?); ?? ?? ??????????????? ?? ?????????????Attributes??attrs?=??new?BasicAttributes(true); ?? ?? ??????????????? ?? ????????????Attribute??personMail??=?new?BasicAttribute("mail"); ?? ?? ?????????????? ?????????????????"xyh@powerise.com.cn"?? ?? ????????????personMail.add("xie@163.com"); ?? ?? ????????????personMail.add("liu@sina.com.cn"); ?? ?? ????????????personMail.add("xyh@powerise.com.cn"); ?? ?? ?????????????attrs.put(personMail); ?? ?? ??????????????? ?? ????????????attrs.put("uid","001"); ?? ?? ?????????????? ?? ????????????attrs.put("cn","jordan1"); ?? ?? ?????????????? ?? ????????????attrs.put("sn","NBA"); ?? ?? ?????????????? ?? ????????????attrs.put("ou","bull"); ?? ?? ????????????System.out.println("bind?object?object?success?"?); ?? ?? ?????????????? ?? ????????????ctx.createSubcontext("uid?=?Jordan,?ou?=?Wizzard,o=NBA",attrs); ?? ?? ????????????? ?? ???????????ctx.close(); ?? ?? ????????}?catch?(NamingException?ex)?{ ?? ?? ???????????System.err.println("bind?object?fail:?"?+?ex.toString()); ?? ?? ????????}?? ?? ?? ???} ?? ?? } ?? ??
2.使用JNDI查找數(shù)據(jù)
前面已經(jīng)介紹了怎么樣將對(duì)象數(shù)據(jù)綁定到服務(wù)器,現(xiàn)在開(kāi)始介紹如何取得調(diào)用綁定在服務(wù)器上的對(duì)象數(shù)據(jù)。
5 例6-2 ?使用JNDI查找數(shù)據(jù)。
要調(diào)用對(duì)象數(shù)據(jù),首先就必須用JNDI查找綁定的對(duì)象和數(shù)據(jù),查找出來(lái)后,再調(diào)用該對(duì)象。程序如下所示。
java 代碼
package?jndi; ?? ?? import?java.nutil.Hashtable; ?? ?? import?javax.naming.Context; ?? ?? import?javax.naming.NamingException; ?? ?? import?javax.naming.NamingEnumeration; ?? ?? import?javax.naming.directory.*; ?? ?? public?class?findUseBindObj?{ ?? ?? public?static?void?main(String[]args){ ?? ?? ??????????? ?? ???????Hashtable?hs?=?new?Hashtable(); ?? ?? ?????????? ?? ????????hs.put(Context.INITIAL_CONTEXT_FACTORY, ?? ?? ???????????????????????"com.sun.jndi.ldap.LdapCtxFactory"); ?? ?? ?????????? ?? ????????hs.put(Context.PROVIDER_URL,?"ldap://localhost:389"); ?? ?? ????????try?{ ?? ?? ????????????? ?? ???????????DirContext?ctx?=?new?InitialDirContext(hs); ?? ?? ???????????? ?? ???????????persons?pers?=(persons)ctx.lookup("uid=Jordan,ou=Bull,o=NBA"); ?? ?? ????????????? ?? ???????????String??age????=??pers.getAge(); ?? ?? ????????????? ?? ???????????String??name??=??pers.getName(); ?? ?? ????????????? ?? ???????System.out.println("name?is?:"?+??name?); ?? ?? ???????? ?? ?? ???????Attributes?attrs=ctx.getAttributes("uid=Jordan,ou=Wizzard,o=NBA"); ?? ?? ????????? ?? ???????for(NamingEnumeration?ae?=?attrs.getAll();ae.hasMore();){ ?? ?? ????????????? ?? ???????????Attribute?attr?=?(Attribute)ae.next(); ?? ?? ???????????System.out.println("Attribute?:?"?+?attr.getID()); ?? ?? ???????????????????????? ?? ????????????for(NamingEnumeration?ve?=?attr.getAll();ve.hasMore();){ ?? ?? ????????????????????????System.out.println("??Value?:?"?+?ve.next()); ?? ?? ????????} ?? ?? ????????} ?? ?? ?????????? ?? ????????System.out.println("find?object?success?"?); ?? ?? ?????????? ?? ???????pers.toString(); ?? ?? ??????????? ?? ?? ????????? ?? ???????ctx.close(); ?? ?? ????}?catch?(NamingException?ex)?{ ?? ?? ???????System.err.println(ex.toString()); ?? ?? ????}????? ?? ?? ??} ?? ?? } ?? ??
對(duì)于作為引用綁定在目錄樹(shù)中的對(duì)象,JNDI SPI 指定針對(duì)引用創(chuàng)建實(shí)際的對(duì)象。因此,在程序中只需要認(rèn)為用lookup()方法返回的對(duì)象就是實(shí)際對(duì)象,而不用在調(diào)用什么方法來(lái)將引用轉(zhuǎn)換為實(shí)際對(duì)象了,因?yàn)樗械墓ぷ鞫加蒍NDI內(nèi)部完成了。
6.2.7? JNDI查詢修改LDAP目錄條目
前面已經(jīng)介紹了如何在LDAP服務(wù)器里存儲(chǔ)一個(gè)對(duì)象:主要是利用一個(gè)DN將對(duì)象綁定到LDAP服務(wù)器中,然后用lookup(DN)查找定位到綁定的對(duì)象,再對(duì)該對(duì)象進(jìn)行操作。但是往往使用DN查找非常難,用戶很難記住DN,因此我們可以使用其他屬性(比如CN=Cherry)來(lái)檢索包含那個(gè)屬性的條目。下面來(lái)介紹JNDI中相關(guān)屬性檢索的具體使用。
1.修改條目
很多時(shí)候可能要對(duì)LDAP服務(wù)器上的條目進(jìn)行修改,如修改用戶密碼,更新應(yīng)用的配置等。但修改必須由一個(gè)已認(rèn)證過(guò)的用戶來(lái)執(zhí)行,而且通常只能修改自己的密碼而不能修改其他信息,管理助手能夠修改電話號(hào)碼和郵件地址,而修改用戶標(biāo)識(shí)這種工作由數(shù)據(jù)庫(kù)管理員完成。
6 例6-3? 用JNDI修改LDAP條目。
java 代碼
package?jndi; ?? ?? import?java.nutil.Hashtable; ?? ?? import?javax.naming.Context; ?? ?? import?javax.naming.directory.Attribute; ?? ?? import?javax.naming.directory.Attributes; ?? ?? import?javax.naming.directory.BasicAttribute; ?? ?? import?javax.naming.directory.DirContext; ?? ?? import?javax.naming.directory.InitialDirContext; ?? ?? import?javax.naming.directory.ModificationItem; ?? ?? public?class?jndiPropertyModify?{ ?? ?? ????public?static?void?main(String[]?args){ ?? ?? ????????Hashtable?hs?=?new?Hashtable(); ?? ?? ?????????? ?? ???????hs.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap. ? ????????????LdapCtxFactory"); ?? ?? ?????????? ?? ????????hs.put(Context.PROVIDER_URL,"ldap://localhost:389"); ?? ?? ?????????? ?? ????????hs.put(Context.SECURITY_AUTHENTICATION,"simple");? ?? ?? ????????hs.put(Context.SECURITY_PRINCIPAL,"uid=Jordan,ou=Bull,o=NBA");? ?? ?? ????????hs.put(Context.SECURITY_CREDENTIALS,"good"); ?? ?? ????????try?{ ?? ?? ???????????? ? ?? ?? ???????????DirContext?ctx?=?new?InitialDirContext(hs); ?? ?? ???????????System.out.println("成功創(chuàng)建初始化context對(duì)象!"); ?? ?? ???????????????? ?? ???????????ModificationItem[]?mdi?=?new?ModificationItem[2]; ?? ?? ???????????????? ?? ???????????Attribute?att0?=?new?BasicAttribute("mail", ?? ???????????????"jordan@163.com"); ?? ?? ???????????????? ?? ???????????Attribute?att1?=?new?BasicAttribute("call","12745827"); ?? ?? ??????????????? ?? ???????????mdi[0]=new?ModificationItem(DirContext.REPLACE_ATTRIBUTE, ?? ???????????????att0);? ?? ?? ?????????????? ?? ???????????mdi[1]=new?ModificationItem(DirContext.ADD_ATTRIBUTE,att1);? ?? ?? ?????????????? ?? ???????????ctx.modifyAttributes("uid=Jordan,ou=Bull,o=NBA",mdi); ?? ?? ????????}catch(Exception?ex?){ ?? ?? ???????????ex.printStackTrace(); ?? ?? ???????????System.exit(1); ?? ?? ????????} ?? ?? ????} ?? ?? } ?? ??
上面程序的作用是修改前面例子中增加的DN為uid = Jordan,ou = Bull,o = NBA條目的屬性。
在程序中用DirContext.REPLACE_ATTRIBUTE來(lái)修改條目的mail屬性,在這里如果原來(lái)的mail屬性有多個(gè)值時(shí),都會(huì)被刪掉,取而代之的是新賦的值。用DirContext. REPLACE_ATTRIBUTE時(shí),如果原來(lái)的屬性(mail)不存在時(shí),就增加一個(gè)屬性,有則修改。
用DirContext.ADD_ATTRIBUTE來(lái)將一個(gè)新屬性增加到條目。真正起到修改作用的是ctx.modifyAttributes("uid = Jordan,ou = Bull,o = NBA",mdi)這條語(yǔ)句。
2.刪除條目
有時(shí),當(dāng)開(kāi)發(fā)者不需要某個(gè)條目時(shí),就可以把它從LDAP服務(wù)器上刪除。這只要通過(guò)調(diào)用參數(shù)為指定DN條目的DirContext接口的destorySubContext()方法來(lái)完成。
7 例6-4? 用JNDI刪除LDAP條目。
java 代碼
package?jndi; ?? ?? import?java.nutil.Hashtable; ?? ?? import?javax.naming.Context; ?? ?? import?javax.naming.directory.Attribute; ?? ?? import?javax.naming.directory.Attributes; ?? ?? import?javax.naming.directory.BasicAttribute; ?? ?? import?javax.naming.directory.DirContext; ?? ?? import?javax.naming.directory.InitialDirContext; ?? ?? import?javax.naming.directory.ModificationItem; ?? ?? public?class?jndiPropertyModify?{ ?? ?? ????public?static?void?main(String[]?args){ ?? ?? ????????Hashtable?hs?=?new?Hashtable(); ?? ?? ?????????? ?? ????????hs.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap. ? ????????????LdapCtxFactory"); ?? ?? ??????????? ?? ????????hs.put(Context.PROVIDER_URL,"ldap://localhost:389"); ?? ?? ??????????? ?? ????????hs.put(Context.SECURITY_AUTHENTICATION,"simple");? ?? ?? ?????????? ?? ????????hs.put(Context.SECURITY_PRINCIPAL,"uid=Jordan,ou=Bull,o=NBA");? ?? ?? ?????????? ?? ????????hs.put(Context.SECURITY_CREDENTIALS,"good"); ?? ?? ????????try?{ ?? ?? ???????????? ? ?? ?? ???????????DirContext?ctx?=?new?InitialDirContext(hs); ?? ?? ??????????????? ?? ???????????ctx.destroySubcontext("uid=Jordan,ou=Bull,o=NBA"); ?? ?? ????????}catch(Exception?ex?){ ?? ?? ???????????ex.printStackTrace(); ?? ?? ???????????System.exit(1); ?? ?? ????????} ?? ?? ????} ?? ?? } ??
總結(jié)
以上是生活随笔為你收集整理的《精通J2EE网络编程》中讲的JNDI 6.2 使用JNDI的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。