午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

開(kāi)發(fā)基于JNDI的應(yīng)用程序

 鬼迷心竅 2006-08-30

Java命名和目錄接口(Java Naming and Directory Interface JNDI)是用于從Java應(yīng)用程序中訪(fǎng)問(wèn)名稱(chēng)和目錄服務(wù)的一組API。命名服務(wù)即將名稱(chēng)與對(duì)象相關(guān)聯(lián),以便能通過(guò)相應(yīng)名稱(chēng)訪(fǎng)問(wèn)這些對(duì)象。而目錄服務(wù)即其對(duì)象具有屬性及名稱(chēng)的命名服務(wù)。

命名或目錄服務(wù)允許您集中管理共享信息的存儲(chǔ),這在網(wǎng)絡(luò)應(yīng)用程序中很重要,因?yàn)樗梢允惯@類(lèi)應(yīng)用程序更加一致和易于管理。例如,可以將打印機(jī)配置存儲(chǔ)在目錄服務(wù)中,這樣所有與打印機(jī)相關(guān)的應(yīng)用程序都能夠使用它。

本文是一份代碼密集型的快速入門(mén)指南,讓您開(kāi)始了解和使用JNDI。它:

  • 提供對(duì)JNDI的綜述。
  • 描述JNDI的特性。
  • 提供使用JNDI開(kāi)發(fā)應(yīng)用程序過(guò)程中的體驗(yàn)。
  • 說(shuō)明如何使用JNDI訪(fǎng)問(wèn) LDAP 服務(wù)器,比如Sun ONE Directory Server 。
  • 說(shuō)明如何使用 JNDI 訪(fǎng)問(wèn)J2EE 服務(wù)。
  • 提供示例代碼,您可以對(duì)其進(jìn)行修改,以用于您自己的應(yīng)用程序。

JNDI綜述

我們所有人每天都在不自知的情況下使用命名服務(wù)。例如,當(dāng)您在瀏覽器中輸入URL http://java. 時(shí),域名系統(tǒng)(Domain Name System DNS)將這個(gè)以符號(hào)表示的URL轉(zhuǎn)換為一個(gè)通信標(biāo)識(shí)符(IP地址)。在命名系統(tǒng)中,對(duì)象的范圍可以從位于DNS記錄中的名稱(chēng)變動(dòng)到應(yīng)用程序服務(wù)器中的企業(yè)JavaBeans組件(Enterprise JavaBeans Components ,EJBs),還可以到輕量級(jí)目錄訪(fǎng)問(wèn)協(xié)議(Lightweight Directory Access Protocol ,LDAP)中的用戶(hù)配置文件。

目錄服務(wù)是命名服務(wù)的自然擴(kuò)展。二者的關(guān)鍵區(qū)別在于,目錄服務(wù)允許屬性(比如用戶(hù)的電子郵件地址)與對(duì)象相關(guān)聯(lián),而命名服務(wù)則不然。這樣,使用目錄服務(wù)時(shí),您可以基于對(duì)象的屬性來(lái)搜索它們。JNDI允許您訪(fǎng)問(wèn)文件系統(tǒng)中的文件,定位遠(yuǎn)程RMI注冊(cè)表中的對(duì)象,訪(fǎng)問(wèn)諸如LDAP這樣的目錄服務(wù),并定位網(wǎng)絡(luò)上的EJB。

很多應(yīng)用程序選擇使用JNDI都可以收到良好的效果,比如LDAP客戶(hù)端、應(yīng)用程序啟動(dòng)器、類(lèi)瀏覽器、網(wǎng)絡(luò)管理實(shí)用工具,或者甚至是地址簿。

JNDI架構(gòu)

JNDI架構(gòu)提供了一個(gè)標(biāo)準(zhǔn)的、與命名系統(tǒng)無(wú)關(guān)的API,這個(gè)API構(gòu)建在特定于命名系統(tǒng)的驅(qū)動(dòng)程序之上。這一層幫助把應(yīng)用程序和實(shí)際的數(shù)據(jù)源隔離開(kāi)來(lái),因此無(wú)論應(yīng)用程序是訪(fǎng)問(wèn)LDAP、RMIDNS還是其他的目錄服務(wù),這都沒(méi)有關(guān)系。換句話(huà)說(shuō),JNDI與任何特定的目錄服務(wù)實(shí)現(xiàn)無(wú)關(guān),您可以使用任何目錄,只要您擁有相應(yīng)的服務(wù)提供程序接口(或驅(qū)動(dòng)程序)即可,如圖1所示。


1: JNDI架構(gòu)

注意,關(guān)于JNDI有一點(diǎn)很重要,即它同時(shí)提供應(yīng)用程序編程接口(Application Programming Interface ,API)和服務(wù)提供程序接口(Service Provider Interface ,SPI)。這樣做的實(shí)際意義在于,對(duì)于您的與命名或目錄服務(wù)交互的應(yīng)用程序來(lái)說(shuō),必須存在用于該服務(wù)的一個(gè)JNDI服務(wù)提供程序,這便是JNDI SPI發(fā)揮作用的舞臺(tái)。一個(gè)服務(wù)提供程序基本上就是一組類(lèi),這些類(lèi)針對(duì)特定的命名和目錄服務(wù)實(shí)現(xiàn)了各種JNDI接口——這與JDBC驅(qū)動(dòng)程序針對(duì)特定的數(shù)據(jù)系統(tǒng)實(shí)現(xiàn)各種JDBC接口極為相似。作為一名應(yīng)用程序開(kāi)發(fā)人員,您不需要擔(dān)心JNDI SPI.。您只需確保,您為每個(gè)想使用的命名或目錄服務(wù)提供了一個(gè)服務(wù)提供程序。

J2SEJNDI

JNDI被包含在Java 2 SDK 1.3 及其更新版本中。它還可以用作JDK 1.11.2的一個(gè)標(biāo)準(zhǔn)擴(kuò)展。 Java 2 SDK 1.4.x的最新版本進(jìn)行了改進(jìn),將以下命名/目錄服務(wù)提供程序包括進(jìn)來(lái):

  • 輕量級(jí)目錄訪(fǎng)問(wèn)協(xié)議Lightweight Directory Access Protocol,LDAP) 服務(wù)提供程序。
  • 公共對(duì)象請(qǐng)求代理架構(gòu)(Common Object Request Broker Architecture ,CORBA)公共對(duì)象服務(wù)(Common Object Services ,COS)命名服務(wù)提供程序。
  • Java遠(yuǎn)程方法調(diào)用( Remote Method Invocation ,RMI)注冊(cè)表服務(wù)提供程序。
  • 域名系統(tǒng) Domain Name System ,DNS) 服務(wù)提供程序。

有關(guān)服務(wù)提供程序的更多內(nèi)容

這里可以下載一系列服務(wù)提供程序。Windows注冊(cè)表JNDI 提供程序(來(lái)自)可能會(huì)引起您特別的興趣,因?yàn)樗试S您訪(fǎng)問(wèn)Windows XP/2000/NT/Me/9x上的注冊(cè)表。

此外,還可以下載JNDI/LDAP Bootster Pack。這個(gè)增強(qiáng)補(bǔ)丁包含對(duì)流行的LDAP控件和擴(kuò)展的支持。它代替了與LDAP 1.2.1服務(wù)提供程序捆綁在一起的增強(qiáng)補(bǔ)丁。參見(jiàn) Controls and Extensions 以獲得更多信息。

另一個(gè)要考察的有趣的服務(wù)提供程序是SunDirectory Services Markup Language (DSML) v2.0提供程序。 DSML的目標(biāo)是將目錄服務(wù)與XML連接起來(lái)

JNDI API

JNDI API 包括5個(gè)包:

  • javax.naming: 包含用于訪(fǎng)問(wèn)命名服務(wù)的類(lèi)和接口。例如,它定義了Context接口,該接口是執(zhí)行查找時(shí)命名服務(wù)的入口點(diǎn)。
  • javax.naming.directory:擴(kuò)展命名包以提供用于訪(fǎng)問(wèn)目錄服務(wù)的類(lèi)和接口。例如,它增加了新的屬性類(lèi),提供代表一個(gè)目錄上下文的DirContext 接口,并且定義了用于檢查和更新與目錄對(duì)象相關(guān)的屬性的方法。
  • javax.naming.event: 當(dāng)訪(fǎng)問(wèn)命名和目錄服務(wù)時(shí),為事件通知提供支持。例如,它定義了一個(gè)NamingEvent類(lèi),用于表示由命名/目錄服務(wù)生成的事件,以及一個(gè)監(jiān)視NamingEvents 類(lèi)的, NamingListener 接口。
  • javax.naming.ldap: 這個(gè)包為LDAP 版本 3 擴(kuò)展操作和空間提供特定的支持,而普通的javax.naming.directory 包沒(méi)有提供這些支持。
  • javax.naming.spi: 這個(gè)包提供方法以動(dòng)態(tài)插入對(duì)通過(guò)javax.naming及其相關(guān)包訪(fǎng)問(wèn)命名和目錄服務(wù)的支持。只有那些對(duì)創(chuàng)建服務(wù)提供程序有著濃厚興趣的開(kāi)發(fā)人員才應(yīng)該對(duì)這個(gè)包感興趣。

JNDI 上下文

承前所述,命名服務(wù)是將名稱(chēng)與對(duì)象相關(guān)聯(lián)。這種關(guān)聯(lián)被稱(chēng)為綁定。一組這樣的綁定被稱(chēng)為上下文,它提供返回對(duì)象的分解或查找操作。其他操作還可能包括綁定與解除綁定名稱(chēng),以及列出被綁定的名稱(chēng)。注意,可以將一個(gè)上下文對(duì)象中的名稱(chēng)綁定到具有同樣命名慣例的另一個(gè)上下文對(duì)象上。這被稱(chēng)為子上下文。例如,如果UNIX目錄/home 是一個(gè)上下文,那么名稱(chēng)與其相關(guān)的目錄便是子上下文。例如,/home/guests.,這里的guests 便是 home個(gè)子上下文。

JNDI中,上下文是使用javax.naming.Context 接口來(lái)表示的,而這個(gè)接口也正是與命名服務(wù)進(jìn)行交互的主要接口。Context (或稍后將要討論的DirContext)接口中的每個(gè)命名方法都有兩種重載的形式:

  • lookup(String name): 接受一個(gè)字符串名稱(chēng)。
  • lookup(javax.naming.Name): 接受一個(gè)結(jié)構(gòu)化的名稱(chēng),比如CompositeName (一個(gè)跨越多個(gè)命名系統(tǒng)的名稱(chēng)) CompondName (一個(gè)位于單個(gè)命名系統(tǒng)中的名稱(chēng));二者均實(shí)現(xiàn)了Name 接口。下面是一個(gè)復(fù)合名稱(chēng)的例子: cn=mydir,cn=Q Mahmoud,ou=People,還有一個(gè)組合名稱(chēng)的例子: cn=mydir,cn=Q Mahmoud,ou=People/myfiles/max.txt (這里的myfiles/max.txt 是代表第二部分的一個(gè)文件名)。

javax.naming.InitialContext 是一個(gè)實(shí)現(xiàn)了 Context接口的類(lèi)。使用這個(gè)類(lèi)作為您到命名服務(wù)的入口點(diǎn)。要?jiǎng)?chuàng)建一個(gè)InitialContext 對(duì)象,構(gòu)造器需要采用一組屬性,形式為java.util.Hashtable 或其子類(lèi)之一,比如Properties.。下面是一個(gè)例子:

Hashtable env = new Hashtable();

// select a service provider factory

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContext");

// create the initial context

Context contxt = new InitialContext(env);

INITIAL_CONTEXT_FACTORY 指定JNDI服務(wù)提供程序中工廠(chǎng)類(lèi)的名稱(chēng)。該工廠(chǎng)負(fù)責(zé)為其服務(wù)創(chuàng)建一個(gè)合適的InitialContext 對(duì)象。在上面的代碼片斷中,指定了用于文件系統(tǒng)服務(wù)提供程序的一個(gè)工廠(chǎng)類(lèi)。表1列出了用于所支持的服務(wù)提供程序的工廠(chǎng)類(lèi)。注意,用于文件系統(tǒng)服務(wù)提供程序的工廠(chǎng)類(lèi)需要Sun Microsystems單獨(dú)下載,它并沒(méi)有與J2SE 1.4.x一起發(fā)行。

1: Context.INITIAL_CONTEXT_FACTORY的值

名稱(chēng)

服務(wù)提供程序工廠(chǎng)

文件系統(tǒng)

com.sun.jndi.fscontext.RefFSContextFactory

LDAP

com.sun.jndi.ldap.LdapCtxFactory

RMI

com.sun.jndi.rmi.registry.RegistryContextFactory

CORBA

com.sun.jndi.cosnaming.CNCtxFactory

DNS

com.sun.jndi.dns.DnsContextFactory

要通過(guò)來(lái)自命名或目錄服務(wù)的名稱(chēng)檢索或解析(查找)一個(gè)對(duì)象,使用Context: Object obj = contxt.lookup(name)lookup方法。lookup 方法返回一個(gè)對(duì)象,該對(duì)象代表您想要查找的上下文的子上下文。

一個(gè)命名的例子

現(xiàn)在,讓我們看一看一個(gè)使用命名服務(wù)的例子。在這個(gè)例子中,我們編寫(xiě)了一個(gè)簡(jiǎn)單的程序,用于查找一個(gè)其名稱(chēng)被當(dāng)作命令行參數(shù)傳入的對(duì)象。在這里,我們將使用一個(gè)用于文件系統(tǒng)的服務(wù)提供程序,而且因此,我們提供作為參數(shù)的名稱(chēng)必須是一個(gè)文件名。示例代碼1中給出了相應(yīng)代碼。

示例代碼 1: Resolve.java

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.naming.NamingException;

import java.util.Hashtable;

public class Resolve {

   public static void main(String argv[]) {

      // The user should provide a file to lookup

      if (argv.length != 1) {

         System.err.println("Usage: java Resolve ");

         System.exit(-1);

      }

      String name = argv[0];

      // Here we use the file system service provider

      Hashtable env = new Hashtable();

      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");

      try {

         // Create the initial context

         Context ctx = new InitialContext(env);

         // Look up an object

         Object obj = ctx.lookup(name);

         // Print it out

         System.out.println(name + " is bound to: " + obj);

         // Close the context

         ctx.close();

      } catch (NamingException e) {

         System.err.println("Problem looking up " + name + ": " + e);

      }

   }

}

在這里,我假定您使用的是Java 2SDK 1.4.x,它附帶有幾個(gè)服務(wù)提供程序(上面已經(jīng)列出)。這個(gè)應(yīng)用程序要使用文件系統(tǒng)服務(wù)提供程序 ,而在默認(rèn)情況下,文件系統(tǒng)服務(wù)提供程序并未安裝。因此,您需要下載并安裝它。另一方面,如果您運(yùn)行這個(gè)程序,而服務(wù)提供程序卻還沒(méi)有被安裝,您將得到一個(gè)NoInitialContextException,意指無(wú)法找到服務(wù)提供程序工廠(chǎng)類(lèi),因此不能初始化這個(gè)類(lèi)。接著,您需要在您的classpath中包括fscontext.jar providerutil.jar——或者像我一樣,您可以簡(jiǎn)單地將這兩個(gè)文件拷貝至JAVA_HOME\jre\lib\ext,這里的 JAVA_HOME 是指您的Java 2SDK安裝的根目錄。

要測(cè)試這個(gè)應(yīng)用程序:

1.         確保您已經(jīng)下載并安裝了文件系統(tǒng)服務(wù)提供程序(正如上一段所講的那樣),因?yàn)檫@個(gè)服務(wù)提供程序并沒(méi)有與J2SE 1.4.x一起提供。

2.         拷貝代碼并將其粘貼到文件中,并將文件命名為Resolve.java。

3.         使用javac 編譯 Resolve.java

4.         使用java 解釋器運(yùn)行應(yīng)用程序。

下面是一次示范運(yùn)行:

prompt> java Resolve \classes

\classes is bound to: com.sun.jndi.fscontext.FSContext@f62373

如果您提供的名稱(chēng)是一個(gè)文件名,您將看到如下結(jié)果:

prompt> java Resolve \classes\Resolve.java

\classes\Resolve.java is bound to: C:\classes\Resolve.java

列出文件目錄的內(nèi)容

現(xiàn)在,讓我們看一看如何使用其他JNDI API列出一個(gè)文件目錄的內(nèi)容。我們假定,您想讓用戶(hù)能夠使用file:///這樣的URL 來(lái)指定命令行參數(shù)。在這種情況下,您要設(shè)置一個(gè)新的屬性PROVIDER_URL,如示例代碼2所示。Context listBindings 方法返回一個(gè) NamingEnumeration對(duì)象,可以通過(guò)使用一個(gè)while 循環(huán)來(lái)迭代這個(gè)對(duì)象,如示例代碼 2所示。

示例代碼 2 Resolve2.java

import javax.naming.Binding;

import javax.naming.NamingEnumeration;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.naming.NamingException;

import java.util.Hashtable;

public class Resolve2 {

   public static void main(String argv[]) {

      // The user should provide a file to lookup

      if (argv.length != 1) {

         System.err.println("Usage: java Resolve2 ");

         System.exit(-1);

      }

      // Here we use the file system service provider

      Hashtable env = new Hashtable();

      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.FSContextFactory");

      env.put(Context.PROVIDER_URL, argv[0]);

      try {

         // Create the initial context

         Context ctx = new InitialContext(env);

         NamingEnumeration ne = ctx.listBindings("");

         while(ne.hasMore()) {

           Binding b = (Binding) ne.next();

           System.out.println(b.getName() + " " + b.getObject());

         }

         // close the context

         ctx.close();

      } catch (NamingException e) {

         System.err.println("Problem looking up " + argv[0] + ": " + e);

      }

   }

}

要測(cè)試這個(gè)應(yīng)用程序,遵照與上一個(gè)例子同樣的編輯和運(yùn)行步驟即可。下面是一次示范運(yùn)行:

prompt>: java Resolve2 file:///uddi

fig1.gif C:\uddi\fig1.gif

fig2.gif C:\uddi\fig2.gif

fig3.gif C:\uddi\fig3.gif

fig4.gif C:\uddi\fig4.gif

fig5.gif C:\uddi\fig5.gif

impl.txt C:\uddi\impl.txt

目錄服務(wù)

承前所述,目錄服務(wù)便是其對(duì)象具有屬性及名稱(chēng)的命名服務(wù)。具有屬性和名稱(chēng)的對(duì)象被稱(chēng)為目錄入口。應(yīng)用程序可以使用目錄服務(wù)存儲(chǔ)和檢索目錄對(duì)象的屬性。它甚至可以被用于對(duì)象存儲(chǔ)。

LDAP

輕量級(jí)目錄訪(fǎng)問(wèn)協(xié)議(LDAP)來(lái)源于X.500 協(xié)議(由位于Ann Arbor的密歇根大學(xué)開(kāi)發(fā)),是一個(gè)用于訪(fǎng)問(wèn)和管理目錄服務(wù)的協(xié)議;它定義了客戶(hù)端應(yīng)該如何訪(fǎng)問(wèn)存儲(chǔ)在服務(wù)器上的數(shù)據(jù),但沒(méi)有定義應(yīng)該如何存儲(chǔ)數(shù)據(jù)。LDAP目錄由帶有描述性信息的入口組成,這些描述性信息描述了人(例如,姓名、電話(huà)號(hào)碼、電子郵件地址,等等)或網(wǎng)絡(luò)資源(比如打印機(jī)、傳真機(jī)之類(lèi)的)。這類(lèi)描述性信息被存儲(chǔ)在一個(gè)入口的屬性中,入口的每個(gè)屬性均描述了一種特定類(lèi)型的信息。下面給出一個(gè)例子,內(nèi)容是用于描述一個(gè)人的屬性:

cn: Qusay H. Mahmoud

mail: qmahmoud@javacourses.com

telephoneNumber: 123-4567

LDAP 目錄服務(wù)可以用于基于屬性查找某個(gè)人的電話(huà)號(hào)碼或電子郵件地址。表2列出了一些常見(jiàn)的LDAP 屬性:

2: 一些常見(jiàn)的 LDAP 屬性

屬性

意義

o

組織

cn

常用名

sn

uid

用戶(hù)id

mail

電子郵件地址

c

國(guó)家

LDAP名稱(chēng)是一個(gè) (名稱(chēng),值) 對(duì)的序列,比如姓名、組織、國(guó)家。

cn=Qusay Mahmoud, o=javacourses.com, c=Canada

javax.naming.directory.DirContext是一個(gè)JNDI的目錄服務(wù)接口,它擴(kuò)展了javax.naming.Context。它提供的方法有:

  • search: 搜索匹配目錄入口的目錄,并比較一個(gè)目錄入口和一組屬性。
  • bind createSubcontext: 添加一個(gè)新的目錄入口。
  • modifyAttributes: 修改一個(gè)目錄入口的特定屬性。rename 方法可以用于修改入口名稱(chēng)本身。
  • unbind destroySubcontext: 刪除一個(gè)特定的目錄入口。
  • close: 結(jié)束與一臺(tái)LDAP服務(wù)器的會(huì)話(huà)。

使用JNDI 進(jìn)行LDAP編程

要操作一臺(tái)LDAP 服務(wù)器(比如Sun ONE Directory Server)中的對(duì)象,您必須首先連接到該服務(wù)器;您可能還需要使您自己通過(guò)服務(wù)器的身份驗(yàn)證。要連接到服務(wù)器,您可以從DirContext 接口獲得對(duì)一個(gè)對(duì)象的引用。使用InitialDirContext 類(lèi)可以做到這一點(diǎn),而該類(lèi)需要一個(gè) Hashtable。

下面的代碼片斷可以使用戶(hù)通過(guò)一臺(tái)LDAP服務(wù)器的身份驗(yàn)證,并連接到該服務(wù)器上。注意,這里使用的是簡(jiǎn)單的身份驗(yàn)證。簡(jiǎn)單身份驗(yàn)證包括把用戶(hù)的完全限定的DN和用戶(hù)的明文口令發(fā)送給LDAP 服務(wù)器。要避免暴露明文口令,使用帶有加密通道的SSL機(jī)制,如果您的LDAP服務(wù)器支持這種機(jī)制的話(huà)。想要了解關(guān)于身份驗(yàn)證模式的更多信息,請(qǐng)參見(jiàn) JNDI Tutorial。

Hashtable env = new Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

// specify where the ldap server is running

env.put(Context.PROVIDER_URL, "ldap://GH308C-N-MAHMOUD.humber.org:61596");

env.put(Context.SECURITY_AUTHENTICATION, "simple");

env.put(Context.SECURITY_PRINCIPAL, "cn=Directory Manager");

env.put(Context.SECURITY_CREDENTIALS, "password");

// Create the initial directory context

DirContext ctx = new InitialDirContext(env);

連接到LDAP 服務(wù)器上之后,您可以在LDAP服務(wù)器上添加新的入口、或者修改、刪除、搜索一個(gè)入口。下面的代碼片斷說(shuō)明了如何添加或存儲(chǔ)一個(gè)新的入口。注意:要存儲(chǔ)一個(gè)對(duì)象,您需要使用Java Schema裝載它,而 Java Schema并沒(méi)有在目錄服務(wù)器上被預(yù)配置。想要了解關(guān)于此點(diǎn)的更多信息,請(qǐng)參見(jiàn)JNDI指南中的Java Objects and the Directory 部分。

SomeObject Obj = new SomeObjct("param1", "param2", "param3");

ctx.bind("cn=myobject", obj);

您可以使用lookup 方法查找一個(gè)對(duì)象,如下:

SomeObject obj = (SomeObject) ctx.lookup("cn=myobject");

示例代碼3 給出了一個(gè)如何檢索命名對(duì)象的屬性的例子。正如您所看到的那樣,用于選擇工廠(chǎng)類(lèi)的代碼與前面相同。我們使用InitialDirContext 類(lèi)創(chuàng)建了一個(gè)目錄上下文DirContext,getAttributes 方法用于返回對(duì)象的屬性,而最后,get方法找到了姓并打印之。相當(dāng)直觀(guān),是不是?

示例代碼 3: GetAttrib.java

import javax.naming.Context;

import javax.naming.directory.InitialDirContext;

import javax.naming.directory.DirContext;

import javax.naming.directory.Attributes;

import javax.naming.NamingException;

import java.util.Hashtable;

class GetAttrib {

   public static void main(String[] argv) {

      Hashtable env = new Hashtable();

      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

      // specify where the ldap server is running

      env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=javacourses.com,c=Canada");

      // use simple authenticate to authenticate the user

      env.put(Context.SECURITY_AUTHENTICATION, "simple");

      env.put(Context.SECURITY_PRINCIPAL, "cn=Directory Manager");

      env.put(Context.SECURITY_CREDENTIALS, "password");

      try {

         // Create the initial directory context

         DirContext ctx = new InitialDirContext(env);

         // Ask for all attributes of the object

         Attributes attrs = ctx.getAttributes("cn=Qusay Mahmoud");

         // Find the surname ("sn") attribute of this object and print it

         System.out.println("Last Name: " + attrs.get("sn").get());

         // Close the context

         ctx.close();

      } catch (NamingException e) {

         System.err.println("Problem getting attribute: " + e);

      }

   }

}

JNDI提供用于進(jìn)行基本和高級(jí)(使用過(guò)濾器)搜索的 API 。例如,使用一組入口必須具有的屬性,以及要在其中執(zhí)行搜索的目標(biāo)上下文,便可以執(zhí)行一次簡(jiǎn)單的搜索。下面的代碼片斷說(shuō)明了如何在棵子樹(shù)中搜索一個(gè)具有uid=qmahmoud 屬性的入口。使用過(guò)濾器的高級(jí)搜索不在本文的討論范圍之內(nèi)。

// ignore attribute name case

Attributes matchattribs = new BasicAttributes(true);

matchattribs.put(new BasicAttribute("uid", "qmahmoud"));

// search for objects with those matching attributes

NamingEnumeration answer = ctx.search("ou=People,o=javacourses.com", matchattribs);

while (answer.hasMore()) {

  SearchResult sr = (SearchResult)answer.next();

  // print the results you need

}

想要了解使用JNDI編寫(xiě)LDAP 客戶(hù)端方面的更多信息,請(qǐng)參見(jiàn)Tips for LDAP Users。

JNDI CORBA COS命名服務(wù)提供程序

CORBA 公共對(duì)象服務(wù) (COS) 名稱(chēng)服務(wù)器用于存儲(chǔ)CORBA對(duì)象引用。您可以使用COS命名包(org.omg.CORBA.CosNaming)CORBA 應(yīng)用程序中訪(fǎng)問(wèn)它。

JNDI COS命名服務(wù)提供程序基于COS命名包實(shí)現(xiàn)javax.naming.Context 接口,這樣CORBA 應(yīng)用程序就能夠使用JNDI訪(fǎng)問(wèn) COS 名稱(chēng)服務(wù)器。因此,使用 JNDI CORBA 應(yīng)用程序具有一個(gè)用于訪(fǎng)問(wèn)所有命名和目錄服務(wù)的接口。這使得CORBA應(yīng)用程序能夠使用像LDAP這樣的分布式企業(yè)級(jí)服務(wù)來(lái)存儲(chǔ)對(duì)象引用。

要選擇COS 命名服務(wù)提供程序,使用:

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");

要轉(zhuǎn)換您的CORBA 應(yīng)用程序以使用JNDI,考慮AddServer.java AddClient.java,它們?cè)?/span>另一篇文章中有更加詳細(xì)的描述。

1.         在客戶(hù)端和服務(wù)器中均使用javax.naming,將:

import org.omg.CosNaming.*;

import org.omg.CosNaming.NamingContextPackage.*;

替換為:

import javax.naming.*;

2.         在客戶(hù)端和服務(wù)器中使用InitialContext 代替 NameService

將:

org.omg.CORBA.Object objRef =

        orb.resolve_initial_references("NameService");

NamingContextExt ncRef =

        NamingContextExtHelper.narrow(objRef);

替換為:

Hashtable env = new Hashtable();

env.put("java.naming.corba.orb", orb);

Context ctx = new InitialContext(env);

3.         使用lookup 代替resolve

:

String name = "Add";

Add href = AddHelper.narrow(ncRef.resolve_str(name));

替換為:

Add href = AddHelper.narrow((org.omg.CORBA.Object)ctx.lookup("Add"));

JNDI RMI 注冊(cè)表服務(wù)提供程序

RMI 注冊(cè)表服務(wù)提供程序允許JNDI 應(yīng)用程序訪(fǎng)問(wèn)使用RMI注冊(cè)表注冊(cè)的遠(yuǎn)程對(duì)象。已知注冊(cè)表所在的位置之后,提供程序使用綁定為注冊(cè)在注冊(cè)表中的對(duì)象創(chuàng)建一個(gè)命名上下文。接下來(lái),這個(gè)上下文可以被綁定到另一個(gè)JNDI可訪(fǎng)問(wèn)的命名空間中,比如LDAP。這項(xiàng)新功能包含了java.rmi.Naming 類(lèi)提供的功能。

這樣使用RMI的主要優(yōu)點(diǎn)是,客戶(hù)端不再需要知道RMI注冊(cè)表運(yùn)行之處的主機(jī)名和端口號(hào);它與位置無(wú)關(guān)。

下面的代碼片斷說(shuō)明了如何將JNDI RMI一起使用:

// select the registry service provider as the initial context

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");

// specify where the registry is running

env.put(Context.PROVIDER_URL, "rmi://server:1099");

// create an initial context that accesses the registry

Context ctx = new InitialContext(env);

// now, the names stored in registry can be listed

NamingEnumeration enum = ctx.list("");

// bind the registry context into LDAP directory

Context ldapctx = (Context)ctx.lookup("ldap://server:port/o=comp,c=ca");

ldapctx.bind("cn=rmi", ctx);

JNDI DNS 服務(wù)提供程序

DNS服務(wù)提供程序使得基于JNDI的應(yīng)用程序能夠訪(fǎng)問(wèn)存儲(chǔ)在DNS中的信息。DNS服務(wù)提供程序?qū)?/span>DNS命名空間呈現(xiàn)為JNDI 目錄上下文的一棵樹(shù),而將DNS 資源記錄呈現(xiàn)為JNDI 屬性。

示例代碼 4 演示了如何使用DNS 服務(wù)提供程序檢索環(huán)境和IP地址(A記錄)的信息。

示例代碼 4: TestDNS.java

import javax.naming.*;

import com.sun.jndi.dns.*;

import java.util.Hashtable;

public class TestDNS {

   public static void main(String[] argv) {

      Name cn = null;

      String name = argv[0];

      Hashtable env = new Hashtable();

      env.put(Context.INITIAL_CONTEXT_FACTORY,

                     "com.sun.jndi.dns.DnsContextFactory");

      env.put(Context.PROVIDER_URL, "dns://IP for DNS Server/");

      try {

         // Create the initial context

         Context ctx = new InitialContext(env);

         // print the fully qualified name (.)

         System.out.println("Name in namespace: "+ctx.getNameInNamespace());

         // retrieve the parser associated with the named context

         NameParser np = ctx.getNameParser(ctx.getNameInNamespace());

          if (argv.length != 1) {

            System.out.println("Usage: java TestDNS ");

            System.exit(-1);

         }

         // parse the name into its components and print them

         cn = np.parse(name);

         System.out.println("Name is: "+cn.toString());

         System.out.println("The parsed name has "+cn.size()+" components:");

         for (int i=0; i<cn.size(); i++){

            System.out.println(cn.get(i));

         }

         System.out.print("Trying to lookup ");

         // get the prefix (domain) and suffix (hostname)

         Name domain = cn.getPrefix(cn.size()-1);

         Name host = cn.getSuffix(cn.size()-1);

         System.out.println("DNS Host: "+host+" Domain: "+domain);

         // retrieve the named object

         Object obj = ctx.lookup(domain);

         System.out.println(domain.toString()+" is bound to: "+obj);

         // retrieve and print the environment in effect

         System.out.println("Domain properties: "+ ((Context)obj).getEnvironment());

         // retrieve and print the IP address (the DNS A records)

         System.out.println("IP for: "+cn+ " is: "+

                            ((DnsContext)obj).getAttributes(host, new String[]{"A"}));

         // we‘re done so close the context

         ctx.close();

      } catch (NamingException e) {

         System.err.println("Problem looking up " + cn + ": " + e);

      }

   }

}

在您運(yùn)行這個(gè)應(yīng)用程序之前,確保您指定了DNS服務(wù)器的IP地址。

下面是一次示范運(yùn)行:

prompt> java TestDNS prep.ai.mit.edu

Name in namespace: .

Name is: prep.ai.mit.edu

The parsed name has 4 components:

edu

mit

ai

prep

Trying to lookup DNS Host: prep Domain: ai.mit.edu

ai.mit.edu is bound to: com.sun.jndi.dns.DnsContext@b89838

Domain properties: {java.naming.provider.url=dns://IP for DNS Server/, java.namin

g.factory.initial=com.sun.jndi.dns.DnsContextFactory}

IP for: prep.ai.mit.edu is: {a=A: 199.232.41.9}

JNDI J2EE

JNDI J2EE平臺(tái)的標(biāo)準(zhǔn)服務(wù)API之一。包含它的目的是為應(yīng)用程序組件提供一個(gè)標(biāo)準(zhǔn)的API,用于引用資源和其他應(yīng)用程序組件。J2EE還定義了一種標(biāo)準(zhǔn)的命名策略(邏輯和真實(shí)的名稱(chēng)),以和 JNDI一起使用,這樣就可以采用一種與部署環(huán)境無(wú)關(guān)的方式編寫(xiě)應(yīng)用程序。您可以引用 J2EE服務(wù),具體方法是根據(jù)其邏輯名稱(chēng)在目錄中查找它們。為了實(shí)現(xiàn)這一點(diǎn),每個(gè)符合 J2EE規(guī)范的系統(tǒng)均提供了一個(gè)稱(chēng)為環(huán)境JNDI 服務(wù),該環(huán)境包括:

  • 環(huán)境變量
  • EJB 引用
  • 資源工廠(chǎng)引用

注意:在這里,我只討論環(huán)境變量。想要了解EJB引用和資源工廠(chǎng)引用方面的更多信息,請(qǐng)參見(jiàn)這篇文章 。

環(huán)境變量

應(yīng)用程序組件的命名環(huán)境允許您定制應(yīng)用程序組件,而不需要訪(fǎng)問(wèn)或修改組件的源代碼。每個(gè)應(yīng)用程序組件定義它自己的環(huán)境入口的集合。同一個(gè)容器中,一個(gè)應(yīng)用程序組件的所有實(shí)例共享同一個(gè)入口。注意,不允許應(yīng)用程序組件實(shí)例在運(yùn)行時(shí)修改環(huán)境。

聲明環(huán)境變量

應(yīng)用程序組件提供程序必須聲明從應(yīng)用程序的組件代碼訪(fǎng)問(wèn)的所有環(huán)境入口。它們是在部署描述器(例如Tomcat中的web.xml)中通過(guò)使用<env-entry> 標(biāo)簽來(lái)聲明的. <env-entry> 標(biāo)簽的元素有:

  • <description>:環(huán)境入口的可選描述。
  • <env-entry-name>: 環(huán)境入口名。
  • <env-entry-type>:期望的環(huán)境變量類(lèi)型。它可以是以下 Java 類(lèi)型之一: Boolean、Byte、 Double、Character、 Float、 Integer、 Long、Short、String 。
  • <env-entry-value>: 必須匹配所提供類(lèi)型的環(huán)境入口值。
  • <env-entry-type>. 這個(gè)值可以稍后改變,但是如果它沒(méi)有被設(shè)定,那么在部署期間必須為它指定一個(gè)值。

示例代碼 5中的例子給出了兩個(gè)環(huán)境入口的聲明。要為一個(gè)新環(huán)境指定一個(gè)聲明,您只要把它添加給您的web應(yīng)用程序描述器 (web.xml) 即可。

示例代碼 5: 聲明環(huán)境變量

<env-entry>

<description>welcome message</description>

<env-entry-name>greetings</env-entry-name>

<env-entry-type>java.lang.String</env-entry-type>

<env-entry-value>Welcome to the Inventory Control

                           System</env-entry-value>

</env-entry>

<env-entry>

<description>maximum number of products</descriptor>

<env-entry-name>inventory/max</env-entry-name>

<env-entry-type>java.lang.Integer</env-entry-type>

<env-entry-value>27</env-entry-value>

</env-entry>

每個(gè)<env-entry>標(biāo)簽描述一個(gè)環(huán)境入口。所以在這個(gè)例子中,定義了兩個(gè)環(huán)境入口。第一個(gè)叫做greetings, 是 String 類(lèi)型,初始的默認(rèn)值為: Welcome to the Inventory Control System。第二個(gè)入口叫做 inventory/max,是 Integer類(lèi)型,初始的默認(rèn)值為 27。

現(xiàn)在,應(yīng)用程序組件實(shí)例可以使用JNDI定位環(huán)境入口. 它使用不帶參數(shù)的構(gòu)造器創(chuàng)建了一個(gè)javax.naming.InitialContext 對(duì)象。接著,它通過(guò)InitialContext查找命名環(huán)境,所使用的是以java:comp/env打頭的JNDI URL。示例代碼 6 說(shuō)明了一個(gè)應(yīng)用程序組件如何訪(fǎng)問(wèn)它的環(huán)境入口。

示例代碼 6: 訪(fǎng)問(wèn)環(huán)境入口

// obtain the application component‘s environment

// naming context

javax.naming.Context ctx =

new javax.naming.InitialContext();

javax.naming.Context env =

ctx.lookup("java:comp/env");

// obtain the greetings message

//configured by the deployer

String str = (String) env.lookup("greetings");

// use the greetings message

System.out.println(greetings);

// obtain the maximum number of products

//configured by the deployer

Integer maximum = (Integer) env.lookup(

"inventory/max");

//use the entry to customize business logic

注意,應(yīng)用程序組件還可以使用如下的完整路徑名查找環(huán)境入口:

javax.naming.Context ctx =

new javax.naming.InitialContext();

String str = (String) ctx.lookup(

"java:comp/env/greetings");

這段代碼片斷可以用在一個(gè) JSP 頁(yè)面中,如 示例代碼 7所示

示例代碼 7: 從一個(gè) JSP頁(yè)面訪(fǎng)問(wèn)環(huán)境入口

<HTML>

<HEAD>

<TITLE>JSP Example</TITLE>

</HEAD>

<BODY BGCOLOR="#ffffcc">

<CENTER>

<H2>Inventory System</H2>

<%

javax.naming.Context ctx =

new javax.naming.InitialContext();

javax.naming.Context myenv =

   (javax.naming.Context) t.lookup("java:comp/env");

java.lang.String s = (java.lang.String)

    myenv.lookup("greetings");

out.println("The value is: "+greetings);

%>

</CENTER>

</BODY>

</HTML>

結(jié)束語(yǔ)

JNDI是使用命名/目錄服務(wù)增強(qiáng)您的網(wǎng)絡(luò)應(yīng)用程序的一組 API。本文通篇給出的例子演示了,開(kāi)始使用JNDI開(kāi)發(fā)基于目錄的應(yīng)用程序是一件多么簡(jiǎn)單的事情。這些例子還演示了 如何使用相同的API訪(fǎng)問(wèn)不同的命名/目錄服務(wù)。開(kāi)發(fā)人員不必學(xué)習(xí)不同的 API。在某些情況下,比如在 RMI 和 CORBA應(yīng)用程序中, JNDI 允許您將命名服務(wù)的選擇延至部署階段。

對(duì)JNDI 未來(lái)的期望有: 與標(biāo)準(zhǔn)的Java SASL API (JSR-28)結(jié)合,支持國(guó)際化的域名,而且支持安全的 DNS 。

要開(kāi)始學(xué)習(xí)和使用JNDI 和 LDAP,下載Sun ONE Directory Server的試用版,它可以用于各種平臺(tái)和各種語(yǔ)言。


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀(guān)點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多