-????????? javax.naming,包含訪問命名服務(wù)的類和接口定義。
-????????? javax.naming.directory,包含訪問目錄服務(wù)的類和接口定義。
-????????? javax.naming.ldap,為ldapv3提供的擴(kuò)展操作提供支持。
-????????? javax.naming.event,為訪問命名和目錄服務(wù)時(shí)的事件通知提供支持。
-????????? javax.naming.spi,為服務(wù)提供商提供的接口,一般用戶不會(huì)涉及。
基本概念
?????? 了解名字服務(wù)和目錄服務(wù)的相關(guān)概念,有助于更好的使用JNDI。
Naming service
?????? 名字服務(wù)定義了如何將名字與對(duì)象關(guān)聯(lián),并通過名字如何找到對(duì)象的方法。典型的例子如:DNS將域名與IP關(guān)聯(lián),文件系統(tǒng)將文件名與文件相關(guān)聯(lián)。在名字服務(wù)中,主要的概念:
-????????? 名字(Names),在名字系統(tǒng)中實(shí)際對(duì)象的代號(hào),如文件名,域名等,它會(huì)被用來查找關(guān)聯(lián)的對(duì)象。不同的系統(tǒng)中會(huì)有不同的命名規(guī)范,如文件系統(tǒng)采用“\”來表示層級(jí),而DNS則使用“.”。
-????????? 綁定(Bindings),名字和實(shí)際對(duì)象的關(guān)聯(lián)。
-????????? 引用和地址(References and Addresses),當(dāng)對(duì)象不能直接被存儲(chǔ)在名字系統(tǒng)時(shí),就必須使用引用,通過引用找到實(shí)際的對(duì)象。在系統(tǒng)中,保存的引用的內(nèi)容被稱為地址。引用還有另一個(gè)用處:在名字系統(tǒng)中,缺少象關(guān)系數(shù)據(jù)庫(kù)中外鍵的概念。通過使用引用,可以作為外鍵的一個(gè)取代辦法。
-????????? 上下文(Context),它是一個(gè)名字-對(duì)象集合,提供了與名字系統(tǒng)交互的主要操作,如查找、綁定、去綁定。子上下文(subcontext)與它的關(guān)系類似文件系統(tǒng)中目錄和子目錄的關(guān)系,子上下文被包含在一個(gè)上下文中,通過父上下文中的一個(gè)名字與子上下文關(guān)聯(lián)。
-????????? 名字系統(tǒng)和名字空間(Naming Systems and Namespaces),名字系統(tǒng)是相同類型的上下文的集合,它提供名字服務(wù);名字空間,是名字系統(tǒng)中的名字集合,如文件系統(tǒng)的文件名和目錄。
Directory service
?????? 目錄服務(wù)是名字服務(wù)的擴(kuò)展,它除了關(guān)聯(lián)名字和對(duì)象,還允許對(duì)象包含屬性。目錄系統(tǒng)通常以層次結(jié)構(gòu)組織數(shù)據(jù)。在目錄服務(wù)中的主要概念:
-????????? 屬性(Attributes),它屬于目錄對(duì)象,它是(名字,值)對(duì),屬性可以有多個(gè)值。
-????????? 目錄和目錄服務(wù)(Directories and Directory Services),目錄是目錄對(duì)象的集合;目錄服務(wù)則提供與目錄相關(guān)的服務(wù),創(chuàng)建、刪除和修改存放在目錄中的對(duì)象的屬性。
-????????? 查找和查找過濾器(Searches and Search Filters),獲取目錄對(duì)象的操作就是查找;過濾器是類似查找條件的對(duì)象。
基本使用
2??????? 注冊(cè)JNDI提供者
在使用JNDI之前,需要先獲取JNDI的提供者,并在系統(tǒng)注冊(cè)它。與JNDI相關(guān)的系統(tǒng)屬性在javax.naming.Context中定義,常用的屬性:
-????????? java.naming.factory.initial,服務(wù)提供者用來創(chuàng)建InitialContext的類名。
-????????? java.naming.provider.url,用來配置InitialContext的初始url
-????????? java.naming.factory.object,用來創(chuàng)建name-to-object映射的類,用于NameClassPair和References。
-????????? java.naming.factory.state,用來創(chuàng)建jndi state的類
對(duì)于目錄服務(wù),由于一般需要安全設(shè)置,還通常使用:
-????????? java.naming.security.authentication,安全類型,三個(gè)值:none,simple或strong。
-????????? java.naming.security.principal,認(rèn)證信息。
-????????? java.naming.security.credentials,證書信息。
-????????? java.naming.security.protocol,安全協(xié)議名。
使用System.setProperty注冊(cè),如果程序不顯示說明,那么java會(huì)在classpath內(nèi)查找jdni.properties文件來完成注冊(cè)。jdni.properties例子:
java.naming.factory.initial=com.codeline.db.MockInitialContextFactory
2??????? 連接服務(wù)
注冊(cè)之后,就可以實(shí)施服務(wù)連接了。對(duì)于名字服務(wù)由InitialContext開始,目錄服務(wù)則使用InitialDirContext。它們分別實(shí)現(xiàn)了Context和DirContext,這兩個(gè)接口分別對(duì)應(yīng)名字服務(wù)和目錄服務(wù)的接口,也是JNDI中最重要的兩個(gè)接口。
-????????? 連接名字服務(wù):
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,"
com.sun.jndi.fscontext.FSContextFactory");
InitialContext ctx = new InitialContext();
-????????? 連接目錄服務(wù):
?? Hashtable env = new Hashtable();
??? env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
??? env.put(Context.PROVIDER_URL, "ldap://myserver.com/");
??? env.put(Context.SECURITY_AUTHENTICATION, "simple");
??? //登錄ldap server需要的用戶名
??? env.put(Context.SECURITY_PRINCIPAL, "ldapuser");
??? //登錄ldap server需要的密碼
??? env.put(Context.SECURITY_CREDENTIALS, "mypassword");
InitialDirContext ctx = new InitialDirContext(env);
-????????? 多服務(wù)提供者:如果應(yīng)用包含多個(gè)服務(wù)提供者,在連接時(shí)略有不同。以名字服務(wù)為例:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL, "rmi://myserver.com:1099");
//使用不同的構(gòu)造函數(shù)
InitialContext ctx = new InitialContext(env);
2??????? 查找對(duì)象
不論名字服務(wù)還是目錄服務(wù),都是使用lookup來查找對(duì)象的。除了可以使用String作為參數(shù)之外,lookup還可使用Name接口作為參數(shù)。
Greeter greeter = (Greeter)ctx.lookup("SayHello");
如果想要獲得上下文中所有的對(duì)象名字,就使用lis返回NameClassPair列表。NameClassPair包含對(duì)象名字和對(duì)象類名。如果想要獲得實(shí)際的對(duì)象實(shí)例列表,就使用listBindings,它返回Binding列表。Binding是NameClassPair的子類,它包含對(duì)象的實(shí)例。
-????????? list
NamingEnumeration list = ctx.list("awt");
while (list.hasMore()) {
??? NameClassPair nc = (NameClassPair)list.next();
??? System.out.println(nc);
}
-????????? listBindings
NamingEnumeration bindings = ctx.listBindings("awt");
while (bindings.hasMore()) {
??? Binding bd = (Binding)bindings.next();
??? System.out.println(bd.getName() + ": " + bd.getObject());
}
2??????? 對(duì)象綁定
-????????? 使用bind添加綁定
Fruit fruit = new Fruit("orange");
ctx.bind("favorite", fruit);
-????????? 使用rebind修改綁定
Fruit fruit = new Fruit("lemon");
ctx.rebind("favorite", fruit);
-????????? 使用unbind去除綁定。
ctx.unbind("favorite");
2??????? 對(duì)象改名
使用rename可以給一個(gè)在上下文中的對(duì)象改名
ctx.rename("report.txt", "old_report.txt");
2??????? 獲取屬性
屬性相關(guān)的接口是Attribute和Attributes,它們都在javax.naming.directory包內(nèi)。通過DirContext的getAttributes方法就可以獲得對(duì)象的屬性集合,然后使用Attributes的get方法獲得對(duì)應(yīng)的屬性,最后通過Attribute的get方法就可以獲得屬性值。
String dn = "uid=me, dc=mycompany, dc=com, ou=customer, o=ExampleApp";
Context user = (Context)ctx.lookup(dn);
//獲得所有屬性
Attributes attrs = user.getAttributes("");
Attribute test= attrs .get("test");
Object testValue= test.get();
上例中獲得的是user的所有屬性,在實(shí)際使用過程中,考慮網(wǎng)絡(luò)帶寬的影響,可以設(shè)置獲取要獲取的屬性列表:
String reqd_attrs = new String[] { "surname", "initials","title", "rfc822mailalias"};
Attributes attrs = user.getAttributes("", reqd_attrs);
2??????? 查找和過濾
使用search方法完成。
?? public DirContext[] findUser(String initials,String surname,String country,String phone) {
??????? //構(gòu)造條件
??????? BasicAttributes search_attrs = new BasicAttributes();
??????? search_attrs.put("initials", initials);
??????? search_attrs.put("sn", surname);
??????? search_attrs.put("c", country);
??????? if(phone != null)
???????? ?search_attrs.put("phonenumber", phone);
?
??????? NamingEnumeration results = initial_ctx.search("ou=Customer,o=ExampleApp", search_attrs);
??????? LinkedList found = new LinkedList();
??????? while(results.hasMore()) {
??????????? SearchResults sr = (SearchResults)results.next();
??????????? String name = sr.getName();
?????? ?????Object ctx = sr.getObject();
??????????? if((ctx == null) || !(ctx instanceof DirContext))
??????????????? found.add(initial_ctx.lookup(name));
??????????? else
??????????????? found.add(ctx);
??????? }
?
??????? DirContext[] ret_val = new DirContext[found.size()];
??????? found.toArray(ret_val);
??????? return ret_val;
?? }
DirContext接口主要過濾方式:
1.使用過濾字符串
String reqd_attrs = new String[] { "cn", "uid","rfc822mailalias" };
NamingEnumeration results = initial_ctx.search("ou=Customer, o=ExampleApp",search_attrs,reqd_attrs);
2.使用SearchControls,獲得更多的控制
SearchControls ctrls = new SearchControls();
ctrls.setCountLimit(20);
ctrls.setTimeLimit(5000);
ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration results = initial_ctx.search("cat=books,ou=Products,
o=ExampleApp","title=*Java*",ctrls);
2??????? 修改屬性
使用DirContext和InitialDirContext的modifyAttributes方法完成。所謂的修改過程,實(shí)際就是先構(gòu)造要修改的屬性列表,然后使用上述方法提交。對(duì)于屬性包含多個(gè)值時(shí),需要把屬性的不修改的值也要包含,否則服務(wù)器會(huì)認(rèn)為那些值不再需要而刪除它們。
public void updateAddress(String dn,String address, String country, String phone) {
??????? BasicAttributes mod_attrs = new BasicAttributes();
??????? if(address != null)
??????????? mod_attrs.put("address", address);
??????? if(country != null)
??????????? mod_attrs.put("c", country);
??????? if(phone != null)
??????????? mod_attrs.put("phonenumber", phone);
??????? if(mod_attrs.size() != 0)
??????????? initial_ctx.modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, mod_attrs);
}
使用ModificationItem,也可一次進(jìn)行多個(gè)不同的修改操作:
ModificationItem[] mod_items = new ModificationItems[2];
Attribute email = new BasicAttribute("rfc822mailalias", new_email);
ModificationItem email_mod = new ModificationItem(DirContext.ADD_ATTRIBUTE, email);
Attribute addr = new BasicAttribute("address", address);
ModificationItem addr_mod = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, addr);
mod_items[0] = email_mod;
mod_items[1] = addr_mod;
initial_ctx.modifyAttributes(dn, mod_items);
2??????? 創(chuàng)建上下文
使用createSubcontext方法完成。
BasicAttributes attrs = new BasicAttributes();
attrs.put("initials", initials);
attrs.put("sn", surname);
attrs.put("rfc822mailalias", email);
if(address != null)
??? attrs.put("address", address);
if(country != null)
??? attrs.put("c", country);
if(phone != null)
??? attrs.put("phonenumber", phone);
initial_ctx.createSubcontext(dn, attrs);
2??????? 刪除上下文
使用destroySubcontext方法完成。
initial_ctx.destroySubcontext(dn);